• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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  */
16 
17 package com.android.internal.app;
18 
19 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
20 import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
21 import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
22 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
23 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
24 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
25 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
26 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
27 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
28 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
29 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
30 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
31 import static android.content.PermissionChecker.PID_UNKNOWN;
32 import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
33 import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
34 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
35 
36 import android.annotation.Nullable;
37 import android.annotation.StringRes;
38 import android.annotation.UiThread;
39 import android.app.Activity;
40 import android.app.ActivityManager;
41 import android.app.ActivityThread;
42 import android.app.VoiceInteractor.PickOptionRequest;
43 import android.app.VoiceInteractor.PickOptionRequest.Option;
44 import android.app.VoiceInteractor.Prompt;
45 import android.app.admin.DevicePolicyEventLogger;
46 import android.app.admin.DevicePolicyManager;
47 import android.compat.annotation.UnsupportedAppUsage;
48 import android.content.BroadcastReceiver;
49 import android.content.ComponentName;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.IntentFilter;
53 import android.content.PermissionChecker;
54 import android.content.pm.ActivityInfo;
55 import android.content.pm.ApplicationInfo;
56 import android.content.pm.PackageManager;
57 import android.content.pm.PackageManager.NameNotFoundException;
58 import android.content.pm.ResolveInfo;
59 import android.content.pm.UserInfo;
60 import android.content.res.Configuration;
61 import android.content.res.TypedArray;
62 import android.graphics.Insets;
63 import android.graphics.drawable.Drawable;
64 import android.net.Uri;
65 import android.os.AsyncTask;
66 import android.os.Build;
67 import android.os.Bundle;
68 import android.os.PatternMatcher;
69 import android.os.RemoteException;
70 import android.os.StrictMode;
71 import android.os.Trace;
72 import android.os.UserHandle;
73 import android.os.UserManager;
74 import android.provider.MediaStore;
75 import android.provider.Settings;
76 import android.stats.devicepolicy.DevicePolicyEnums;
77 import android.text.TextUtils;
78 import android.util.Log;
79 import android.util.Slog;
80 import android.view.Gravity;
81 import android.view.LayoutInflater;
82 import android.view.View;
83 import android.view.ViewGroup;
84 import android.view.ViewGroup.LayoutParams;
85 import android.view.Window;
86 import android.view.WindowInsets;
87 import android.view.WindowManager;
88 import android.widget.AbsListView;
89 import android.widget.AdapterView;
90 import android.widget.Button;
91 import android.widget.FrameLayout;
92 import android.widget.ImageView;
93 import android.widget.ListView;
94 import android.widget.Space;
95 import android.widget.TabHost;
96 import android.widget.TabWidget;
97 import android.widget.TextView;
98 import android.widget.Toast;
99 
100 import com.android.internal.R;
101 import com.android.internal.annotations.VisibleForTesting;
102 import com.android.internal.app.AbstractMultiProfilePagerAdapter.CompositeEmptyStateProvider;
103 import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
104 import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
105 import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
106 import com.android.internal.app.AbstractMultiProfilePagerAdapter.OnSwitchOnWorkSelectedListener;
107 import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
108 import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
109 import com.android.internal.app.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
110 import com.android.internal.app.chooser.ChooserTargetInfo;
111 import com.android.internal.app.chooser.DisplayResolveInfo;
112 import com.android.internal.app.chooser.TargetInfo;
113 import com.android.internal.content.PackageMonitor;
114 import com.android.internal.logging.MetricsLogger;
115 import com.android.internal.logging.nano.MetricsProto;
116 import com.android.internal.util.LatencyTracker;
117 import com.android.internal.widget.ResolverDrawerLayout;
118 import com.android.internal.widget.ViewPager;
119 
120 import java.util.ArrayList;
121 import java.util.Arrays;
122 import java.util.Iterator;
123 import java.util.List;
124 import java.util.Objects;
125 import java.util.Set;
126 
127 /**
128  * This activity is displayed when the system attempts to start an Intent for
129  * which there is more than one matching activity, allowing the user to decide
130  * which to go to.  It is not normally used directly by application developers.
131  */
132 @UiThread
133 public class ResolverActivity extends Activity implements
134         ResolverListAdapter.ResolverListCommunicator {
135 
136     @UnsupportedAppUsage
ResolverActivity()137     public ResolverActivity() {
138         mIsIntentPicker = getClass().equals(ResolverActivity.class);
139     }
140 
ResolverActivity(boolean isIntentPicker)141     protected ResolverActivity(boolean isIntentPicker) {
142         mIsIntentPicker = isIntentPicker;
143     }
144 
145     private boolean mSafeForwardingMode;
146     private Button mAlwaysButton;
147     private Button mOnceButton;
148     protected View mProfileView;
149     private int mLastSelected = AbsListView.INVALID_POSITION;
150     private boolean mResolvingHome = false;
151     private String mProfileSwitchMessage;
152     private int mLayoutId;
153     @VisibleForTesting
154     protected final ArrayList<Intent> mIntents = new ArrayList<>();
155     private PickTargetOptionRequest mPickOptionRequest;
156     private String mReferrerPackage;
157     private CharSequence mTitle;
158     private int mDefaultTitleResId;
159     // Expected to be true if this object is ResolverActivity or is ResolverWrapperActivity.
160     private final boolean mIsIntentPicker;
161 
162     // Whether or not this activity supports choosing a default handler for the intent.
163     @VisibleForTesting
164     protected boolean mSupportsAlwaysUseOption;
165     protected ResolverDrawerLayout mResolverDrawerLayout;
166     @UnsupportedAppUsage
167     protected PackageManager mPm;
168     protected int mLaunchedFromUid;
169 
170     private static final String TAG = "ResolverActivity";
171     private static final boolean DEBUG = false;
172     private static final String LAST_SHOWN_TAB_KEY = "last_shown_tab_key";
173 
174     private boolean mRegistered;
175 
176     protected Insets mSystemWindowInsets = null;
177     private Space mFooterSpacer = null;
178 
179     /** See {@link #setRetainInOnStop}. */
180     private boolean mRetainInOnStop;
181 
182     private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
183     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
184     private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
185     protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
186     protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
187 
188     /** Tracks if we should ignore future broadcasts telling us the work profile is enabled */
189     private boolean mWorkProfileHasBeenEnabled = false;
190 
191     @VisibleForTesting
192     public static boolean ENABLE_TABBED_VIEW = true;
193     private static final String TAB_TAG_PERSONAL = "personal";
194     private static final String TAB_TAG_WORK = "work";
195 
196     private PackageMonitor mPersonalPackageMonitor;
197     private PackageMonitor mWorkPackageMonitor;
198 
199     @VisibleForTesting
200     protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
201 
202     protected QuietModeManager mQuietModeManager;
203 
204     // Intent extra for connected audio devices
205     public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
206 
207     /**
208      * Integer extra to indicate which profile should be automatically selected.
209      * <p>Can only be used if there is a work profile.
210      * <p>Possible values can be either {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}.
211      */
212     protected static final String EXTRA_SELECTED_PROFILE =
213             "com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE";
214 
215     /**
216      * {@link UserHandle} extra to indicate the user of the user that the starting intent
217      * originated from.
218      * <p>This is not necessarily the same as {@link #getUserId()} or {@link UserHandle#myUserId()},
219      * as there are edge cases when the intent resolver is launched in the other profile.
220      * For example, when we have 0 resolved apps in current profile and multiple resolved
221      * apps in the other profile, opening a link from the current profile launches the intent
222      * resolver in the other one. b/148536209 for more info.
223      */
224     static final String EXTRA_CALLING_USER =
225             "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER";
226 
227     protected static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
228     protected static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK;
229 
230     private BroadcastReceiver mWorkProfileStateReceiver;
231     private UserHandle mHeaderCreatorUser;
232 
233     private UserHandle mWorkProfileUserHandle;
234 
235     @Nullable
236     private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
237 
238     protected final LatencyTracker mLatencyTracker = getLatencyTracker();
239 
getLatencyTracker()240     private LatencyTracker getLatencyTracker() {
241         return LatencyTracker.getInstance(this);
242     }
243 
244     /**
245      * Get the string resource to be used as a label for the link to the resolver activity for an
246      * action.
247      *
248      * @param action The action to resolve
249      *
250      * @return The string resource to be used as a label
251      */
getLabelRes(String action)252     public static @StringRes int getLabelRes(String action) {
253         return ActionTitle.forAction(action).labelRes;
254     }
255 
256     private enum ActionTitle {
257         VIEW(Intent.ACTION_VIEW,
258                 com.android.internal.R.string.whichViewApplication,
259                 com.android.internal.R.string.whichViewApplicationNamed,
260                 com.android.internal.R.string.whichViewApplicationLabel),
261         EDIT(Intent.ACTION_EDIT,
262                 com.android.internal.R.string.whichEditApplication,
263                 com.android.internal.R.string.whichEditApplicationNamed,
264                 com.android.internal.R.string.whichEditApplicationLabel),
265         SEND(Intent.ACTION_SEND,
266                 com.android.internal.R.string.whichSendApplication,
267                 com.android.internal.R.string.whichSendApplicationNamed,
268                 com.android.internal.R.string.whichSendApplicationLabel),
269         SENDTO(Intent.ACTION_SENDTO,
270                 com.android.internal.R.string.whichSendToApplication,
271                 com.android.internal.R.string.whichSendToApplicationNamed,
272                 com.android.internal.R.string.whichSendToApplicationLabel),
273         SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
274                 com.android.internal.R.string.whichSendApplication,
275                 com.android.internal.R.string.whichSendApplicationNamed,
276                 com.android.internal.R.string.whichSendApplicationLabel),
277         CAPTURE_IMAGE(MediaStore.ACTION_IMAGE_CAPTURE,
278                 com.android.internal.R.string.whichImageCaptureApplication,
279                 com.android.internal.R.string.whichImageCaptureApplicationNamed,
280                 com.android.internal.R.string.whichImageCaptureApplicationLabel),
281         DEFAULT(null,
282                 com.android.internal.R.string.whichApplication,
283                 com.android.internal.R.string.whichApplicationNamed,
284                 com.android.internal.R.string.whichApplicationLabel),
285         HOME(Intent.ACTION_MAIN,
286                 com.android.internal.R.string.whichHomeApplication,
287                 com.android.internal.R.string.whichHomeApplicationNamed,
288                 com.android.internal.R.string.whichHomeApplicationLabel);
289 
290         // titles for layout that deals with http(s) intents
291         public static final int BROWSABLE_TITLE_RES =
292                 com.android.internal.R.string.whichOpenLinksWith;
293         public static final int BROWSABLE_HOST_TITLE_RES =
294                 com.android.internal.R.string.whichOpenHostLinksWith;
295         public static final int BROWSABLE_HOST_APP_TITLE_RES =
296                 com.android.internal.R.string.whichOpenHostLinksWithApp;
297         public static final int BROWSABLE_APP_TITLE_RES =
298                 com.android.internal.R.string.whichOpenLinksWithApp;
299 
300         public final String action;
301         public final int titleRes;
302         public final int namedTitleRes;
303         public final @StringRes int labelRes;
304 
ActionTitle(String action, int titleRes, int namedTitleRes, @StringRes int labelRes)305         ActionTitle(String action, int titleRes, int namedTitleRes, @StringRes int labelRes) {
306             this.action = action;
307             this.titleRes = titleRes;
308             this.namedTitleRes = namedTitleRes;
309             this.labelRes = labelRes;
310         }
311 
forAction(String action)312         public static ActionTitle forAction(String action) {
313             for (ActionTitle title : values()) {
314                 if (title != HOME && action != null && action.equals(title.action)) {
315                     return title;
316                 }
317             }
318             return DEFAULT;
319         }
320     }
321 
createPackageMonitor(ResolverListAdapter listAdapter)322     protected PackageMonitor createPackageMonitor(ResolverListAdapter listAdapter) {
323         return new PackageMonitor() {
324             @Override
325             public void onSomePackagesChanged() {
326                 listAdapter.handlePackagesChanged();
327                 updateProfileViewButton();
328             }
329 
330             @Override
331             public boolean onPackageChanged(String packageName, int uid, String[] components) {
332                 // We care about all package changes, not just the whole package itself which is
333                 // default behavior.
334                 return true;
335             }
336         };
337     }
338 
339     private Intent makeMyIntent() {
340         Intent intent = new Intent(getIntent());
341         intent.setComponent(null);
342         // The resolver activity is set to be hidden from recent tasks.
343         // we don't want this attribute to be propagated to the next activity
344         // being launched.  Note that if the original Intent also had this
345         // flag set, we are now losing it.  That should be a very rare case
346         // and we can live with this.
347         intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
348         return intent;
349     }
350 
351     /**
352      * Call {@link Activity#onCreate} without initializing anything further. This should
353      * only be used when the activity is about to be immediately finished to avoid wasting
354      * initializing steps and leaking resources.
355      */
356     protected void super_onCreate(Bundle savedInstanceState) {
357         super.onCreate(savedInstanceState);
358     }
359 
360     @Override
361     protected void onCreate(Bundle savedInstanceState) {
362         // Use a specialized prompt when we're handling the 'Home' app startActivity()
363         final Intent intent = makeMyIntent();
364         final Set<String> categories = intent.getCategories();
365         if (Intent.ACTION_MAIN.equals(intent.getAction())
366                 && categories != null
367                 && categories.size() == 1
368                 && categories.contains(Intent.CATEGORY_HOME)) {
369             // Note: this field is not set to true in the compatibility version.
370             mResolvingHome = true;
371         }
372 
373         setSafeForwardingMode(true);
374 
375         onCreate(savedInstanceState, intent, null, 0, null, null, true);
376     }
377 
378     /**
379      * Compatibility version for other bundled services that use this overload without
380      * a default title resource
381      */
382     @UnsupportedAppUsage
383     protected void onCreate(Bundle savedInstanceState, Intent intent,
384             CharSequence title, Intent[] initialIntents,
385             List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
386         onCreate(savedInstanceState, intent, title, 0, initialIntents, rList,
387                 supportsAlwaysUseOption);
388     }
389 
390     protected void onCreate(Bundle savedInstanceState, Intent intent,
391             CharSequence title, int defaultTitleRes, Intent[] initialIntents,
392             List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
393         setTheme(appliedThemeResId());
394         super.onCreate(savedInstanceState);
395 
396         mQuietModeManager = createQuietModeManager();
397 
398         // Determine whether we should show that intent is forwarded
399         // from managed profile to owner or other way around.
400         setProfileSwitchMessage(intent.getContentUserHint());
401 
402         mLaunchedFromUid = getLaunchedFromUid();
403         if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
404             // Gulp!
405             finish();
406             return;
407         }
408 
409         mPm = getPackageManager();
410 
411         mReferrerPackage = getReferrerPackageName();
412 
413         // Add our initial intent as the first item, regardless of what else has already been added.
414         mIntents.add(0, new Intent(intent));
415         mTitle = title;
416         mDefaultTitleResId = defaultTitleRes;
417 
418         mSupportsAlwaysUseOption = supportsAlwaysUseOption;
419         mWorkProfileUserHandle = fetchWorkProfileUserProfile();
420 
421         // The last argument of createResolverListAdapter is whether to do special handling
422         // of the last used choice to highlight it in the list.  We need to always
423         // turn this off when running under voice interaction, since it results in
424         // a more complicated UI that the current voice interaction flow is not able
425         // to handle. We also turn it off when the work tab is shown to simplify the UX.
426         boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction()
427                 && !shouldShowTabs();
428         mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
429         if (configureContentView()) {
430             return;
431         }
432 
433         mPersonalPackageMonitor = createPackageMonitor(
434                 mMultiProfilePagerAdapter.getPersonalListAdapter());
435         mPersonalPackageMonitor.register(
436                 this, getMainLooper(), getPersonalProfileUserHandle(), false);
437         if (shouldShowTabs()) {
438             mWorkPackageMonitor = createPackageMonitor(
439                     mMultiProfilePagerAdapter.getWorkListAdapter());
440             mWorkPackageMonitor.register(this, getMainLooper(), getWorkProfileUserHandle(), false);
441         }
442 
443         mRegistered = true;
444 
445         final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
446         if (rdl != null) {
447             rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
448                 @Override
449                 public void onDismissed() {
450                     finish();
451                 }
452             });
453 
454             boolean hasTouchScreen = getPackageManager()
455                     .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
456 
457             if (isVoiceInteraction() || !hasTouchScreen) {
458                 rdl.setCollapsed(false);
459             }
460 
461             rdl.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
462                     | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
463             rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
464 
465             mResolverDrawerLayout = rdl;
466         }
467 
468         mProfileView = findViewById(R.id.profile_button);
469         if (mProfileView != null) {
470             mProfileView.setOnClickListener(this::onProfileClick);
471             updateProfileViewButton();
472         }
473 
474         final Set<String> categories = intent.getCategories();
475         MetricsLogger.action(this, mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
476                 ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
477                 : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
478                 intent.getAction() + ":" + intent.getType() + ":"
479                         + (categories != null ? Arrays.toString(categories.toArray()) : ""));
480     }
481 
482     protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
483             Intent[] initialIntents,
484             List<ResolveInfo> rList,
485             boolean filterLastUsed) {
486         AbstractMultiProfilePagerAdapter resolverMultiProfilePagerAdapter = null;
487         if (shouldShowTabs()) {
488             resolverMultiProfilePagerAdapter =
489                     createResolverMultiProfilePagerAdapterForTwoProfiles(
490                             initialIntents, rList, filterLastUsed);
491         } else {
492             resolverMultiProfilePagerAdapter = createResolverMultiProfilePagerAdapterForOneProfile(
493                     initialIntents, rList, filterLastUsed);
494         }
495         return resolverMultiProfilePagerAdapter;
496     }
497 
498     @VisibleForTesting
499     protected MyUserIdProvider createMyUserIdProvider() {
500         return new MyUserIdProvider();
501     }
502 
503     @VisibleForTesting
504     protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
505         return new CrossProfileIntentsChecker(getContentResolver());
506     }
507 
508     @VisibleForTesting
509     protected QuietModeManager createQuietModeManager() {
510         UserManager userManager = getSystemService(UserManager.class);
511         return new QuietModeManager() {
512 
513             private boolean mIsWaitingToEnableWorkProfile = false;
514 
515             @Override
516             public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
517                 return userManager.isQuietModeEnabled(workProfileUserHandle);
518             }
519 
520             @Override
521             public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
522                 AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
523                     userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
524                 });
525                 mIsWaitingToEnableWorkProfile = true;
526             }
527 
528             @Override
529             public void markWorkProfileEnabledBroadcastReceived() {
530                 mIsWaitingToEnableWorkProfile = false;
531             }
532 
533             @Override
534             public boolean isWaitingToEnableWorkProfile() {
535                 return mIsWaitingToEnableWorkProfile;
536             }
537         };
538     }
539 
540     protected EmptyStateProvider createBlockerEmptyStateProvider() {
541         final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser());
542 
543         if (!shouldShowNoCrossProfileIntentsEmptyState) {
544             // Implementation that doesn't show any blockers
545             return new EmptyStateProvider() {};
546         }
547 
548         final AbstractMultiProfilePagerAdapter.EmptyState
549                 noWorkToPersonalEmptyState =
550                 new DevicePolicyBlockerEmptyState(/* context= */ this,
551                 /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
552                 /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
553                 /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_PERSONAL,
554                 /* defaultSubtitleResource= */
555                 R.string.resolver_cant_access_personal_apps_explanation,
556                 /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL,
557                 /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
558 
559         final AbstractMultiProfilePagerAdapter.EmptyState noPersonalToWorkEmptyState =
560                 new DevicePolicyBlockerEmptyState(/* context= */ this,
561                 /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
562                 /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
563                 /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_WORK,
564                 /* defaultSubtitleResource= */
565                 R.string.resolver_cant_access_work_apps_explanation,
566                 /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK,
567                 /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
568 
569         return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
570                 noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
571                 createCrossProfileIntentsChecker(), createMyUserIdProvider());
572     }
573 
574     protected EmptyStateProvider createEmptyStateProvider(
575             @Nullable UserHandle workProfileUserHandle) {
576         final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
577 
578         final EmptyStateProvider workProfileOffEmptyStateProvider =
579                 new WorkProfilePausedEmptyStateProvider(this, workProfileUserHandle,
580                         mQuietModeManager,
581                         /* onSwitchOnWorkSelectedListener= */
582                         () -> { if (mOnSwitchOnWorkSelectedListener != null) {
583                             mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
584                         }},
585                         getMetricsCategory());
586 
587         final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
588                 this,
589                 workProfileUserHandle,
590                 getPersonalProfileUserHandle(),
591                 getMetricsCategory(),
592                 createMyUserIdProvider()
593         );
594 
595         // Return composite provider, the order matters (the higher, the more priority)
596         return new CompositeEmptyStateProvider(
597                 blockerEmptyStateProvider,
598                 workProfileOffEmptyStateProvider,
599                 noAppsEmptyStateProvider
600         );
601     }
602 
603     private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
604             Intent[] initialIntents,
605             List<ResolveInfo> rList, boolean filterLastUsed) {
606         ResolverListAdapter adapter = createResolverListAdapter(
607                 /* context */ this,
608                 /* payloadIntents */ mIntents,
609                 initialIntents,
610                 rList,
611                 filterLastUsed,
612                 /* userHandle */ UserHandle.of(UserHandle.myUserId()));
613         QuietModeManager quietModeManager = createQuietModeManager();
614         return new ResolverMultiProfilePagerAdapter(
615                 /* context */ this,
616                 adapter,
617                 createEmptyStateProvider(/* workProfileUserHandle= */ null),
618                 quietModeManager,
619                 /* workProfileUserHandle= */ null);
620     }
621 
622     private UserHandle getIntentUser() {
623         return getIntent().hasExtra(EXTRA_CALLING_USER)
624                 ? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
625                 : getUser();
626     }
627 
628     private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
629             Intent[] initialIntents,
630             List<ResolveInfo> rList,
631             boolean filterLastUsed) {
632         // In the edge case when we have 0 apps in the current profile and >1 apps in the other,
633         // the intent resolver is started in the other profile. Since this is the only case when
634         // this happens, we check for it here and set the current profile's tab.
635         int selectedProfile = getCurrentProfile();
636         UserHandle intentUser = getIntentUser();
637         if (!getUser().equals(intentUser)) {
638             if (getPersonalProfileUserHandle().equals(intentUser)) {
639                 selectedProfile = PROFILE_PERSONAL;
640             } else if (getWorkProfileUserHandle().equals(intentUser)) {
641                 selectedProfile = PROFILE_WORK;
642             }
643         } else {
644             int selectedProfileExtra = getSelectedProfileExtra();
645             if (selectedProfileExtra != -1) {
646                 selectedProfile = selectedProfileExtra;
647             }
648         }
649         // We only show the default app for the profile of the current user. The filterLastUsed
650         // flag determines whether to show a default app and that app is not shown in the
651         // resolver list. So filterLastUsed should be false for the other profile.
652         ResolverListAdapter personalAdapter = createResolverListAdapter(
653                 /* context */ this,
654                 /* payloadIntents */ mIntents,
655                 selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
656                 rList,
657                 (filterLastUsed && UserHandle.myUserId()
658                         == getPersonalProfileUserHandle().getIdentifier()),
659                 /* userHandle */ getPersonalProfileUserHandle());
660         UserHandle workProfileUserHandle = getWorkProfileUserHandle();
661         ResolverListAdapter workAdapter = createResolverListAdapter(
662                 /* context */ this,
663                 /* payloadIntents */ mIntents,
664                 selectedProfile == PROFILE_WORK ? initialIntents : null,
665                 rList,
666                 (filterLastUsed && UserHandle.myUserId()
667                         == workProfileUserHandle.getIdentifier()),
668                 /* userHandle */ workProfileUserHandle);
669         QuietModeManager quietModeManager = createQuietModeManager();
670         return new ResolverMultiProfilePagerAdapter(
671                 /* context */ this,
672                 personalAdapter,
673                 workAdapter,
674                 createEmptyStateProvider(getWorkProfileUserHandle()),
675                 quietModeManager,
676                 selectedProfile,
677                 getWorkProfileUserHandle());
678     }
679 
680     protected int appliedThemeResId() {
681         return R.style.Theme_DeviceDefault_Resolver;
682     }
683 
684     /**
685      * Returns {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK} if the {@link
686      * #EXTRA_SELECTED_PROFILE} extra was supplied, or {@code -1} if no extra was supplied.
687      * @throws IllegalArgumentException if the value passed to the {@link #EXTRA_SELECTED_PROFILE}
688      * extra is not {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}
689      */
690     int getSelectedProfileExtra() {
691         int selectedProfile = -1;
692         if (getIntent().hasExtra(EXTRA_SELECTED_PROFILE)) {
693             selectedProfile = getIntent().getIntExtra(EXTRA_SELECTED_PROFILE, /* defValue = */ -1);
694             if (selectedProfile != PROFILE_PERSONAL && selectedProfile != PROFILE_WORK) {
695                 throw new IllegalArgumentException(EXTRA_SELECTED_PROFILE + " has invalid value "
696                         + selectedProfile + ". Must be either ResolverActivity.PROFILE_PERSONAL or "
697                         + "ResolverActivity.PROFILE_WORK.");
698             }
699         }
700         return selectedProfile;
701     }
702 
703     protected @Profile int getCurrentProfile() {
704         return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
705     }
706 
707     protected UserHandle getPersonalProfileUserHandle() {
708         return UserHandle.of(ActivityManager.getCurrentUser());
709     }
710     protected @Nullable UserHandle getWorkProfileUserHandle() {
711         return mWorkProfileUserHandle;
712     }
713 
714     protected @Nullable UserHandle fetchWorkProfileUserProfile() {
715         mWorkProfileUserHandle = null;
716         UserManager userManager = getSystemService(UserManager.class);
717         for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) {
718             if (userInfo.isManagedProfile()) {
719                 mWorkProfileUserHandle = userInfo.getUserHandle();
720             }
721         }
722         return mWorkProfileUserHandle;
723     }
724 
725     private boolean hasWorkProfile() {
726         return getWorkProfileUserHandle() != null;
727     }
728 
729     protected boolean shouldShowTabs() {
730         return hasWorkProfile() && ENABLE_TABBED_VIEW;
731     }
732 
733     protected void onProfileClick(View v) {
734         final DisplayResolveInfo dri =
735                 mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile();
736         if (dri == null) {
737             return;
738         }
739 
740         // Do not show the profile switch message anymore.
741         mProfileSwitchMessage = null;
742 
743         onTargetSelected(dri, false);
744         finish();
745     }
746 
747     /**
748      * Numerous layouts are supported, each with optional ViewGroups.
749      * Make sure the inset gets added to the correct View, using
750      * a footer for Lists so it can properly scroll under the navbar.
751      */
752     protected boolean shouldAddFooterView() {
753         if (useLayoutWithDefault()) return true;
754 
755         View buttonBar = findViewById(R.id.button_bar);
756         if (buttonBar == null || buttonBar.getVisibility() == View.GONE) return true;
757 
758         return false;
759     }
760 
761     protected void applyFooterView(int height) {
762         if (mFooterSpacer == null) {
763             mFooterSpacer = new Space(getApplicationContext());
764         } else {
765             ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
766                 .getActiveAdapterView().removeFooterView(mFooterSpacer);
767         }
768         mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
769                                                                    mSystemWindowInsets.bottom));
770         ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
771             .getActiveAdapterView().addFooterView(mFooterSpacer);
772     }
773 
774     protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
775         mSystemWindowInsets = insets.getSystemWindowInsets();
776 
777         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
778                 mSystemWindowInsets.right, 0);
779 
780         resetButtonBar();
781 
782         if (shouldUseMiniResolver()) {
783             View buttonContainer = findViewById(R.id.button_bar_container);
784             buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom
785                     + getResources().getDimensionPixelOffset(R.dimen.resolver_button_bar_spacing));
786         }
787 
788         // Need extra padding so the list can fully scroll up
789         if (shouldAddFooterView()) {
790             applyFooterView(mSystemWindowInsets.bottom);
791         }
792 
793         return insets.consumeSystemWindowInsets();
794     }
795 
796     @Override
797     public void onConfigurationChanged(Configuration newConfig) {
798         super.onConfigurationChanged(newConfig);
799         mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
800         if (mIsIntentPicker && shouldShowTabs() && !useLayoutWithDefault()
801                 && !shouldUseMiniResolver()) {
802             updateIntentPickerPaddings();
803         }
804 
805         if (mSystemWindowInsets != null) {
806             mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
807                     mSystemWindowInsets.right, 0);
808         }
809     }
810 
811     private void updateIntentPickerPaddings() {
812         View titleCont = findViewById(R.id.title_container);
813         titleCont.setPadding(
814                 titleCont.getPaddingLeft(),
815                 titleCont.getPaddingTop(),
816                 titleCont.getPaddingRight(),
817                 getResources().getDimensionPixelSize(R.dimen.resolver_title_padding_bottom));
818         View buttonBar = findViewById(R.id.button_bar);
819         buttonBar.setPadding(
820                 buttonBar.getPaddingLeft(),
821                 getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing),
822                 buttonBar.getPaddingRight(),
823                 getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing));
824     }
825 
826     @Override // ResolverListCommunicator
827     public void sendVoiceChoicesIfNeeded() {
828         if (!isVoiceInteraction()) {
829             // Clearly not needed.
830             return;
831         }
832 
833         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getCount();
834         final Option[] options = new Option[count];
835         for (int i = 0, N = options.length; i < N; i++) {
836             TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter().getItem(i);
837             if (target == null) {
838                 // If this occurs, a new set of targets is being loaded. Let that complete,
839                 // and have the next call to send voice choices proceed instead.
840                 return;
841             }
842             options[i] = optionForChooserTarget(target, i);
843         }
844 
845         mPickOptionRequest = new PickTargetOptionRequest(
846                 new Prompt(getTitle()), options, null);
847         getVoiceInteractor().submitRequest(mPickOptionRequest);
848     }
849 
850     Option optionForChooserTarget(TargetInfo target, int index) {
851         return new Option(target.getDisplayLabel(), index);
852     }
853 
854     protected final void setAdditionalTargets(Intent[] intents) {
855         if (intents != null) {
856             for (Intent intent : intents) {
857                 mIntents.add(intent);
858             }
859         }
860     }
861 
862     @Override // SelectableTargetInfoCommunicator ResolverListCommunicator
863     public Intent getTargetIntent() {
864         return mIntents.isEmpty() ? null : mIntents.get(0);
865     }
866 
867     protected String getReferrerPackageName() {
868         final Uri referrer = getReferrer();
869         if (referrer != null && "android-app".equals(referrer.getScheme())) {
870             return referrer.getHost();
871         }
872         return null;
873     }
874 
875     public int getLayoutResource() {
876         return R.layout.resolver_list;
877     }
878 
879     @Override // ResolverListCommunicator
880     public void updateProfileViewButton() {
881         if (mProfileView == null) {
882             return;
883         }
884 
885         final DisplayResolveInfo dri =
886                 mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile();
887         if (dri != null && !shouldShowTabs()) {
888             mProfileView.setVisibility(View.VISIBLE);
889             View text = mProfileView.findViewById(R.id.profile_button);
890             if (!(text instanceof TextView)) {
891                 text = mProfileView.findViewById(R.id.text1);
892             }
893             ((TextView) text).setText(dri.getDisplayLabel());
894         } else {
895             mProfileView.setVisibility(View.GONE);
896         }
897     }
898 
899     private void setProfileSwitchMessage(int contentUserHint) {
900         if (contentUserHint != UserHandle.USER_CURRENT &&
901                 contentUserHint != UserHandle.myUserId()) {
902             UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
903             UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
904             boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
905                     : false;
906             boolean targetIsManaged = userManager.isManagedProfile();
907             if (originIsManaged && !targetIsManaged) {
908                 mProfileSwitchMessage = getForwardToPersonalMsg();
909             } else if (!originIsManaged && targetIsManaged) {
910                 mProfileSwitchMessage = getForwardToWorkMsg();
911             }
912         }
913     }
914 
915     private String getForwardToPersonalMsg() {
916         return getSystemService(DevicePolicyManager.class).getResources().getString(
917                 FORWARD_INTENT_TO_PERSONAL,
918                 () -> getString(com.android.internal.R.string.forward_intent_to_owner));
919     }
920 
921     private String getForwardToWorkMsg() {
922         return getSystemService(DevicePolicyManager.class).getResources().getString(
923                 FORWARD_INTENT_TO_WORK,
924                 () -> getString(com.android.internal.R.string.forward_intent_to_work));
925     }
926 
927     /**
928      * Turn on launch mode that is safe to use when forwarding intents received from
929      * applications and running in system processes.  This mode uses Activity.startActivityAsCaller
930      * instead of the normal Activity.startActivity for launching the activity selected
931      * by the user.
932      *
933      * <p>This mode is set to true by default if the activity is initialized through
934      * {@link #onCreate(android.os.Bundle)}.  If a subclass calls one of the other onCreate
935      * methods, it is set to false by default.  You must set it before calling one of the
936      * more detailed onCreate methods, so that it will be set correctly in the case where
937      * there is only one intent to resolve and it is thus started immediately.</p>
938      */
939     public void setSafeForwardingMode(boolean safeForwarding) {
940         mSafeForwardingMode = safeForwarding;
941     }
942 
943     protected CharSequence getTitleForAction(Intent intent, int defaultTitleRes) {
944         final ActionTitle title = mResolvingHome
945                 ? ActionTitle.HOME
946                 : ActionTitle.forAction(intent.getAction());
947 
948         // While there may already be a filtered item, we can only use it in the title if the list
949         // is already sorted and all information relevant to it is already in the list.
950         final boolean named =
951                 mMultiProfilePagerAdapter.getActiveListAdapter().getFilteredPosition() >= 0;
952         if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
953             return getString(defaultTitleRes);
954         } else {
955             return named
956                     ? getString(title.namedTitleRes, mMultiProfilePagerAdapter
957                             .getActiveListAdapter().getFilteredItem().getDisplayLabel())
958                     : getString(title.titleRes);
959         }
960     }
961 
962     void dismiss() {
963         if (!isFinishing()) {
964             finish();
965         }
966     }
967 
968     @Override
969     protected void onRestart() {
970         super.onRestart();
971         if (!mRegistered) {
972             mPersonalPackageMonitor.register(this, getMainLooper(),
973                     getPersonalProfileUserHandle(), false);
974             if (shouldShowTabs()) {
975                 if (mWorkPackageMonitor == null) {
976                     mWorkPackageMonitor = createPackageMonitor(
977                             mMultiProfilePagerAdapter.getWorkListAdapter());
978                 }
979                 mWorkPackageMonitor.register(this, getMainLooper(),
980                         getWorkProfileUserHandle(), false);
981             }
982             mRegistered = true;
983         }
984         if (shouldShowTabs() && mQuietModeManager.isWaitingToEnableWorkProfile()) {
985             if (mQuietModeManager.isQuietModeEnabled(getWorkProfileUserHandle())) {
986                 mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
987             }
988         }
989         mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
990         updateProfileViewButton();
991     }
992 
993     @Override
994     protected void onStart() {
995         super.onStart();
996 
997         this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
998         if (shouldShowTabs()) {
999             mWorkProfileStateReceiver = createWorkProfileStateReceiver();
1000             registerWorkProfileStateReceiver();
1001 
1002             mWorkProfileHasBeenEnabled = isWorkProfileEnabled();
1003         }
1004     }
1005 
1006     private boolean isWorkProfileEnabled() {
1007         UserHandle workUserHandle = getWorkProfileUserHandle();
1008         UserManager userManager = getSystemService(UserManager.class);
1009 
1010         return !userManager.isQuietModeEnabled(workUserHandle)
1011                 && userManager.isUserUnlocked(workUserHandle);
1012     }
1013 
1014     private void registerWorkProfileStateReceiver() {
1015         IntentFilter filter = new IntentFilter();
1016         filter.addAction(Intent.ACTION_USER_UNLOCKED);
1017         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
1018         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1019         registerReceiverAsUser(mWorkProfileStateReceiver, UserHandle.ALL, filter, null, null);
1020     }
1021 
1022     @Override
1023     protected void onStop() {
1024         super.onStop();
1025 
1026         final Window window = this.getWindow();
1027         final WindowManager.LayoutParams attrs = window.getAttributes();
1028         attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
1029         window.setAttributes(attrs);
1030 
1031         if (mRegistered) {
1032             mPersonalPackageMonitor.unregister();
1033             if (mWorkPackageMonitor != null) {
1034                 mWorkPackageMonitor.unregister();
1035             }
1036             mRegistered = false;
1037         }
1038         final Intent intent = getIntent();
1039         if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
1040                 && !mResolvingHome && !mRetainInOnStop) {
1041             // This resolver is in the unusual situation where it has been
1042             // launched at the top of a new task.  We don't let it be added
1043             // to the recent tasks shown to the user, and we need to make sure
1044             // that each time we are launched we get the correct launching
1045             // uid (not re-using the same resolver from an old launching uid),
1046             // so we will now finish ourself since being no longer visible,
1047             // the user probably can't get back to us.
1048             if (!isChangingConfigurations()) {
1049                 finish();
1050             }
1051         }
1052         if (mWorkPackageMonitor != null) {
1053             unregisterReceiver(mWorkProfileStateReceiver);
1054             mWorkPackageMonitor = null;
1055         }
1056     }
1057 
1058     @Override
1059     protected void onDestroy() {
1060         super.onDestroy();
1061         if (!isChangingConfigurations() && mPickOptionRequest != null) {
1062             mPickOptionRequest.cancel();
1063         }
1064         if (mMultiProfilePagerAdapter != null
1065                 && mMultiProfilePagerAdapter.getActiveListAdapter() != null) {
1066             mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy();
1067         }
1068     }
1069 
1070     @Override
1071     protected void onSaveInstanceState(Bundle outState) {
1072         super.onSaveInstanceState(outState);
1073         ViewPager viewPager = findViewById(R.id.profile_pager);
1074         if (viewPager != null) {
1075             outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
1076         }
1077     }
1078 
1079     @Override
1080     protected void onRestoreInstanceState(Bundle savedInstanceState) {
1081         super.onRestoreInstanceState(savedInstanceState);
1082         resetButtonBar();
1083         ViewPager viewPager = findViewById(R.id.profile_pager);
1084         if (viewPager != null) {
1085             viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
1086         }
1087         mMultiProfilePagerAdapter.clearInactiveProfileCache();
1088     }
1089 
1090     private boolean hasManagedProfile() {
1091         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
1092         if (userManager == null) {
1093             return false;
1094         }
1095 
1096         try {
1097             List<UserInfo> profiles = userManager.getProfiles(getUserId());
1098             for (UserInfo userInfo : profiles) {
1099                 if (userInfo != null && userInfo.isManagedProfile()) {
1100                     return true;
1101                 }
1102             }
1103         } catch (SecurityException e) {
1104             return false;
1105         }
1106         return false;
1107     }
1108 
1109     private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
1110         try {
1111             ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
1112                     resolveInfo.activityInfo.packageName, 0 /* default flags */);
1113             return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
1114         } catch (NameNotFoundException e) {
1115             return false;
1116         }
1117     }
1118 
1119     private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
1120             boolean filtered) {
1121         if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
1122             // Never allow the inactive profile to always open an app.
1123             mAlwaysButton.setEnabled(false);
1124             return;
1125         }
1126         boolean enabled = false;
1127         ResolveInfo ri = null;
1128         if (hasValidSelection) {
1129             ri = mMultiProfilePagerAdapter.getActiveListAdapter()
1130                     .resolveInfoForPosition(checkedPos, filtered);
1131             if (ri == null) {
1132                 Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
1133                 return;
1134             } else if (ri.targetUserId != UserHandle.USER_CURRENT) {
1135                 Log.e(TAG, "Attempted to set selection to resolve info for another user");
1136                 return;
1137             } else {
1138                 enabled = true;
1139             }
1140 
1141             mAlwaysButton.setText(getResources()
1142                     .getString(R.string.activity_resolver_use_always));
1143         }
1144 
1145         if (ri != null) {
1146             ActivityInfo activityInfo = ri.activityInfo;
1147 
1148             boolean hasRecordPermission =
1149                     mPm.checkPermission(android.Manifest.permission.RECORD_AUDIO,
1150                             activityInfo.packageName)
1151                             == android.content.pm.PackageManager.PERMISSION_GRANTED;
1152 
1153             if (!hasRecordPermission) {
1154                 // OK, we know the record permission, is this a capture device
1155                 boolean hasAudioCapture =
1156                         getIntent().getBooleanExtra(
1157                                 ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
1158                 enabled = !hasAudioCapture;
1159             }
1160         }
1161         mAlwaysButton.setEnabled(enabled);
1162     }
1163 
1164     public void onButtonClick(View v) {
1165         final int id = v.getId();
1166         ListView listView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
1167         ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
1168         int which = currentListAdapter.hasFilteredItem()
1169                 ? currentListAdapter.getFilteredPosition()
1170                 : listView.getCheckedItemPosition();
1171         boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem();
1172         startSelected(which, id == R.id.button_always, hasIndexBeenFiltered);
1173     }
1174 
1175     public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
1176         if (isFinishing()) {
1177             return;
1178         }
1179         ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
1180                 .resolveInfoForPosition(which, hasIndexBeenFiltered);
1181         if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
1182             Toast.makeText(this,
1183                     getWorkProfileNotSupportedMsg(
1184                             ri.activityInfo.loadLabel(getPackageManager()).toString()),
1185                     Toast.LENGTH_LONG).show();
1186             return;
1187         }
1188 
1189         TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
1190                 .targetInfoForPosition(which, hasIndexBeenFiltered);
1191         if (target == null) {
1192             return;
1193         }
1194         if (onTargetSelected(target, always)) {
1195             if (always && mSupportsAlwaysUseOption) {
1196                 MetricsLogger.action(
1197                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
1198             } else if (mSupportsAlwaysUseOption) {
1199                 MetricsLogger.action(
1200                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
1201             } else {
1202                 MetricsLogger.action(
1203                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
1204             }
1205             MetricsLogger.action(this,
1206                     mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
1207                             ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
1208                             : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
1209             finish();
1210         }
1211     }
1212 
1213     private String getWorkProfileNotSupportedMsg(String launcherName) {
1214         return getSystemService(DevicePolicyManager.class).getResources().getString(
1215                 RESOLVER_WORK_PROFILE_NOT_SUPPORTED,
1216                 () -> getString(
1217                         com.android.internal.R.string.activity_resolver_work_profiles_support,
1218                         launcherName),
1219                 launcherName);
1220     }
1221 
1222     /**
1223      * Replace me in subclasses!
1224      */
1225     @Override // ResolverListCommunicator
1226     public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
1227         return defIntent;
1228     }
1229 
1230     @Override // ResolverListCommunicator
1231     public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing,
1232             boolean rebuildCompleted) {
1233         if (isDestroyed()) {
1234             return;
1235         }
1236         if (isAutolaunching()) {
1237             return;
1238         }
1239         if (mIsIntentPicker) {
1240             ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
1241                     .setUseLayoutWithDefault(useLayoutWithDefault());
1242         }
1243         if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) {
1244             mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter);
1245         } else {
1246             mMultiProfilePagerAdapter.showListView(listAdapter);
1247         }
1248         // showEmptyResolverListEmptyState can mark the tab as loaded,
1249         // which is a precondition for auto launching
1250         if (rebuildCompleted && maybeAutolaunchActivity()) {
1251             return;
1252         }
1253         if (doPostProcessing) {
1254             maybeCreateHeader(listAdapter);
1255             resetButtonBar();
1256             onListRebuilt(listAdapter, rebuildCompleted);
1257         }
1258     }
1259 
1260     protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) {
1261         final ItemClickListener listener = new ItemClickListener();
1262         setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
1263         if (shouldShowTabs() && mIsIntentPicker) {
1264             final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
1265             if (rdl != null) {
1266                 rdl.setMaxCollapsedHeight(getResources()
1267                         .getDimensionPixelSize(useLayoutWithDefault()
1268                                 ? R.dimen.resolver_max_collapsed_height_with_default_with_tabs
1269                                 : R.dimen.resolver_max_collapsed_height_with_tabs));
1270             }
1271         }
1272     }
1273 
1274     protected boolean onTargetSelected(TargetInfo target, boolean always) {
1275         final ResolveInfo ri = target.getResolveInfo();
1276         final Intent intent = target != null ? target.getResolvedIntent() : null;
1277 
1278         if (intent != null && (mSupportsAlwaysUseOption
1279                 || mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem())
1280                 && mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList() != null) {
1281             // Build a reasonable intent filter, based on what matched.
1282             IntentFilter filter = new IntentFilter();
1283             Intent filterIntent;
1284 
1285             if (intent.getSelector() != null) {
1286                 filterIntent = intent.getSelector();
1287             } else {
1288                 filterIntent = intent;
1289             }
1290 
1291             String action = filterIntent.getAction();
1292             if (action != null) {
1293                 filter.addAction(action);
1294             }
1295             Set<String> categories = filterIntent.getCategories();
1296             if (categories != null) {
1297                 for (String cat : categories) {
1298                     filter.addCategory(cat);
1299                 }
1300             }
1301             filter.addCategory(Intent.CATEGORY_DEFAULT);
1302 
1303             int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
1304             Uri data = filterIntent.getData();
1305             if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
1306                 String mimeType = filterIntent.resolveType(this);
1307                 if (mimeType != null) {
1308                     try {
1309                         filter.addDataType(mimeType);
1310                     } catch (IntentFilter.MalformedMimeTypeException e) {
1311                         Log.w("ResolverActivity", e);
1312                         filter = null;
1313                     }
1314                 }
1315             }
1316             if (data != null && data.getScheme() != null) {
1317                 // We need the data specification if there was no type,
1318                 // OR if the scheme is not one of our magical "file:"
1319                 // or "content:" schemes (see IntentFilter for the reason).
1320                 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
1321                         || (!"file".equals(data.getScheme())
1322                                 && !"content".equals(data.getScheme()))) {
1323                     filter.addDataScheme(data.getScheme());
1324 
1325                     // Look through the resolved filter to determine which part
1326                     // of it matched the original Intent.
1327                     Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
1328                     if (pIt != null) {
1329                         String ssp = data.getSchemeSpecificPart();
1330                         while (ssp != null && pIt.hasNext()) {
1331                             PatternMatcher p = pIt.next();
1332                             if (p.match(ssp)) {
1333                                 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
1334                                 break;
1335                             }
1336                         }
1337                     }
1338                     Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
1339                     if (aIt != null) {
1340                         while (aIt.hasNext()) {
1341                             IntentFilter.AuthorityEntry a = aIt.next();
1342                             if (a.match(data) >= 0) {
1343                                 int port = a.getPort();
1344                                 filter.addDataAuthority(a.getHost(),
1345                                         port >= 0 ? Integer.toString(port) : null);
1346                                 break;
1347                             }
1348                         }
1349                     }
1350                     pIt = ri.filter.pathsIterator();
1351                     if (pIt != null) {
1352                         String path = data.getPath();
1353                         while (path != null && pIt.hasNext()) {
1354                             PatternMatcher p = pIt.next();
1355                             if (p.match(path)) {
1356                                 filter.addDataPath(p.getPath(), p.getType());
1357                                 break;
1358                             }
1359                         }
1360                     }
1361                 }
1362             }
1363 
1364             if (filter != null) {
1365                 final int N = mMultiProfilePagerAdapter.getActiveListAdapter()
1366                         .getUnfilteredResolveList().size();
1367                 ComponentName[] set;
1368                 // If we don't add back in the component for forwarding the intent to a managed
1369                 // profile, the preferred activity may not be updated correctly (as the set of
1370                 // components we tell it we knew about will have changed).
1371                 final boolean needToAddBackProfileForwardingComponent =
1372                         mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null;
1373                 if (!needToAddBackProfileForwardingComponent) {
1374                     set = new ComponentName[N];
1375                 } else {
1376                     set = new ComponentName[N + 1];
1377                 }
1378 
1379                 int bestMatch = 0;
1380                 for (int i=0; i<N; i++) {
1381                     ResolveInfo r = mMultiProfilePagerAdapter.getActiveListAdapter()
1382                             .getUnfilteredResolveList().get(i).getResolveInfoAt(0);
1383                     set[i] = new ComponentName(r.activityInfo.packageName,
1384                             r.activityInfo.name);
1385                     if (r.match > bestMatch) bestMatch = r.match;
1386                 }
1387 
1388                 if (needToAddBackProfileForwardingComponent) {
1389                     set[N] = mMultiProfilePagerAdapter.getActiveListAdapter()
1390                             .getOtherProfile().getResolvedComponentName();
1391                     final int otherProfileMatch = mMultiProfilePagerAdapter.getActiveListAdapter()
1392                             .getOtherProfile().getResolveInfo().match;
1393                     if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch;
1394                 }
1395 
1396                 if (always) {
1397                     final int userId = getUserId();
1398                     final PackageManager pm = getPackageManager();
1399 
1400                     // Set the preferred Activity
1401                     pm.addUniquePreferredActivity(filter, bestMatch, set, intent.getComponent());
1402 
1403                     if (ri.handleAllWebDataURI) {
1404                         // Set default Browser if needed
1405                         final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId);
1406                         if (TextUtils.isEmpty(packageName)) {
1407                             pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId);
1408                         }
1409                     }
1410                 } else {
1411                     try {
1412                         mMultiProfilePagerAdapter.getActiveListAdapter()
1413                                 .mResolverListController.setLastChosen(intent, filter, bestMatch);
1414                     } catch (RemoteException re) {
1415                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
1416                     }
1417                 }
1418             }
1419         }
1420 
1421         if (target != null) {
1422             safelyStartActivity(target);
1423 
1424             // Rely on the ActivityManager to pop up a dialog regarding app suspension
1425             // and return false
1426             if (target.isSuspended()) {
1427                 return false;
1428             }
1429         }
1430 
1431         return true;
1432     }
1433 
1434     @VisibleForTesting
1435     public void safelyStartActivity(TargetInfo cti) {
1436         // We're dispatching intents that might be coming from legacy apps, so
1437         // don't kill ourselves.
1438         StrictMode.disableDeathOnFileUriExposure();
1439         try {
1440             UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
1441             safelyStartActivityInternal(cti, currentUserHandle, null);
1442         } finally {
1443             StrictMode.enableDeathOnFileUriExposure();
1444         }
1445     }
1446 
1447     /**
1448      * Start activity as a fixed user handle.
1449      * @param cti TargetInfo to be launched.
1450      * @param user User to launch this activity as.
1451      */
1452     @VisibleForTesting
1453     public void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
1454         safelyStartActivityAsUser(cti, user, null);
1455     }
1456 
1457     protected void safelyStartActivityAsUser(
1458             TargetInfo cti, UserHandle user, @Nullable Bundle options) {
1459         // We're dispatching intents that might be coming from legacy apps, so
1460         // don't kill ourselves.
1461         StrictMode.disableDeathOnFileUriExposure();
1462         try {
1463             safelyStartActivityInternal(cti, user, options);
1464         } finally {
1465             StrictMode.enableDeathOnFileUriExposure();
1466         }
1467     }
1468 
1469     private void safelyStartActivityInternal(
1470             TargetInfo cti, UserHandle user, @Nullable Bundle options) {
1471         // If the target is suspended, the activity will not be successfully launched.
1472         // Do not unregister from package manager updates in this case
1473         if (!cti.isSuspended() && mRegistered) {
1474             if (mPersonalPackageMonitor != null) {
1475                 mPersonalPackageMonitor.unregister();
1476             }
1477             if (mWorkPackageMonitor != null) {
1478                 mWorkPackageMonitor.unregister();
1479             }
1480             mRegistered = false;
1481         }
1482         // If needed, show that intent is forwarded
1483         // from managed profile to owner or other way around.
1484         if (mProfileSwitchMessage != null) {
1485             Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
1486         }
1487         if (!mSafeForwardingMode) {
1488             if (cti.startAsUser(this, options, user)) {
1489                 onActivityStarted(cti);
1490                 maybeLogCrossProfileTargetLaunch(cti, user);
1491             }
1492             return;
1493         }
1494         try {
1495             if (cti.startAsCaller(this, options, user.getIdentifier())) {
1496                 onActivityStarted(cti);
1497                 maybeLogCrossProfileTargetLaunch(cti, user);
1498             }
1499         } catch (RuntimeException e) {
1500             Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
1501                     + " package " + getLaunchedFromPackage() + ", while running in "
1502                     + ActivityThread.currentProcessName(), e);
1503         }
1504     }
1505 
1506     private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
1507         if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
1508             return;
1509         }
1510         DevicePolicyEventLogger
1511                 .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
1512                 .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
1513                 .setStrings(getMetricsCategory(),
1514                         cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
1515                 .write();
1516     }
1517 
1518 
1519     public void onActivityStarted(TargetInfo cti) {
1520         // Do nothing
1521     }
1522 
1523     @Override // ResolverListCommunicator
1524     public boolean shouldGetActivityMetadata() {
1525         return false;
1526     }
1527 
1528     public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
1529         return !target.isSuspended();
1530     }
1531 
1532     void showTargetDetails(ResolveInfo ri) {
1533         Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
1534                 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
1535                 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
1536         startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
1537     }
1538 
1539     @VisibleForTesting
1540     protected ResolverListAdapter createResolverListAdapter(Context context,
1541             List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
1542             boolean filterLastUsed, UserHandle userHandle) {
1543         Intent startIntent = getIntent();
1544         boolean isAudioCaptureDevice =
1545                 startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
1546         return new ResolverListAdapter(context, payloadIntents, initialIntents, rList,
1547                 filterLastUsed, createListController(userHandle), this,
1548                 isAudioCaptureDevice);
1549     }
1550 
1551     @VisibleForTesting
1552     protected ResolverListController createListController(UserHandle userHandle) {
1553         return new ResolverListController(
1554                 this,
1555                 mPm,
1556                 getTargetIntent(),
1557                 getReferrerPackageName(),
1558                 mLaunchedFromUid,
1559                 userHandle);
1560     }
1561 
1562     /**
1563      * Sets up the content view.
1564      * @return <code>true</code> if the activity is finishing and creation should halt.
1565      */
1566     private boolean configureContentView() {
1567         if (mMultiProfilePagerAdapter.getActiveListAdapter() == null) {
1568             throw new IllegalStateException("mMultiProfilePagerAdapter.getCurrentListAdapter() "
1569                     + "cannot be null.");
1570         }
1571         Trace.beginSection("configureContentView");
1572         // We partially rebuild the inactive adapter to determine if we should auto launch
1573         // isTabLoaded will be true here if the empty state screen is shown instead of the list.
1574         boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true)
1575                 || mMultiProfilePagerAdapter.getActiveListAdapter().isTabLoaded();
1576         if (shouldShowTabs()) {
1577             boolean rebuildInactiveCompleted = mMultiProfilePagerAdapter.rebuildInactiveTab(false)
1578                     || mMultiProfilePagerAdapter.getInactiveListAdapter().isTabLoaded();
1579             rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted;
1580         }
1581 
1582         if (shouldUseMiniResolver()) {
1583             configureMiniResolverContent();
1584             Trace.endSection();
1585             return false;
1586         }
1587 
1588         if (useLayoutWithDefault()) {
1589             mLayoutId = R.layout.resolver_list_with_default;
1590         } else {
1591             mLayoutId = getLayoutResource();
1592         }
1593         setContentView(mLayoutId);
1594         mMultiProfilePagerAdapter.setupViewPager(findViewById(R.id.profile_pager));
1595         boolean result = postRebuildList(rebuildCompleted);
1596         Trace.endSection();
1597         return result;
1598     }
1599 
1600     /**
1601      * Mini resolver is shown when the user is choosing between browser[s] in this profile and a
1602      * single app in the other profile (see shouldUseMiniResolver()). It shows the single app icon
1603      * and asks the user if they'd like to open that cross-profile app or use the in-profile
1604      * browser.
1605      */
1606     private void configureMiniResolverContent() {
1607         mLayoutId = R.layout.miniresolver;
1608         setContentView(mLayoutId);
1609 
1610         DisplayResolveInfo sameProfileResolveInfo =
1611                 mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0);
1612         boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK;
1613 
1614         final ResolverListAdapter inactiveAdapter =
1615                 mMultiProfilePagerAdapter.getInactiveListAdapter();
1616         final DisplayResolveInfo otherProfileResolveInfo = inactiveAdapter.mDisplayList.get(0);
1617 
1618         // Load the icon asynchronously
1619         ImageView icon = findViewById(R.id.icon);
1620         inactiveAdapter.new LoadIconTask(otherProfileResolveInfo) {
1621             @Override
1622             protected void onPostExecute(Drawable drawable) {
1623                 if (!isDestroyed()) {
1624                     otherProfileResolveInfo.setDisplayIcon(drawable);
1625                     new ResolverListAdapter.ViewHolder(icon).bindIcon(otherProfileResolveInfo);
1626                 }
1627             }
1628         }.execute();
1629 
1630         ((TextView) findViewById(R.id.open_cross_profile)).setText(
1631                 getResources().getString(
1632                         inWorkProfile ? R.string.miniresolver_open_in_personal
1633                                 : R.string.miniresolver_open_in_work,
1634                         otherProfileResolveInfo.getDisplayLabel()));
1635         ((Button) findViewById(R.id.use_same_profile_browser)).setText(
1636                 inWorkProfile ? R.string.miniresolver_use_work_browser
1637                         : R.string.miniresolver_use_personal_browser);
1638 
1639         findViewById(R.id.use_same_profile_browser).setOnClickListener(
1640                 v -> {
1641                     safelyStartActivity(sameProfileResolveInfo);
1642                     finish();
1643                 });
1644 
1645         findViewById(R.id.button_open).setOnClickListener(v -> {
1646             Intent intent = otherProfileResolveInfo.getResolvedIntent();
1647             safelyStartActivityAsUser(otherProfileResolveInfo,
1648                     inactiveAdapter.mResolverListController.getUserHandle());
1649             finish();
1650         });
1651     }
1652 
1653     /**
1654      * Mini resolver should be used when all of the following are true:
1655      * 1. This is the intent picker (ResolverActivity).
1656      * 2. This profile only has web browser matches.
1657      * 3. The other profile has a single non-browser match.
1658      */
1659     private boolean shouldUseMiniResolver() {
1660         if (!mIsIntentPicker) {
1661             return false;
1662         }
1663         if (mMultiProfilePagerAdapter.getActiveListAdapter() == null
1664                 || mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
1665             return false;
1666         }
1667         List<DisplayResolveInfo> sameProfileList =
1668                 mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList;
1669         List<DisplayResolveInfo> otherProfileList =
1670                 mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList;
1671 
1672         if (sameProfileList.isEmpty()) {
1673             Log.d(TAG, "No targets in the current profile");
1674             return false;
1675         }
1676 
1677         if (otherProfileList.size() != 1) {
1678             Log.d(TAG, "Found " + otherProfileList.size() + " resolvers in the other profile");
1679             return false;
1680         }
1681 
1682         if (otherProfileList.get(0).getResolveInfo().handleAllWebDataURI) {
1683             Log.d(TAG, "Other profile is a web browser");
1684             return false;
1685         }
1686 
1687         for (DisplayResolveInfo info : sameProfileList) {
1688             if (!info.getResolveInfo().handleAllWebDataURI) {
1689                 Log.d(TAG, "Non-browser found in this profile");
1690                 return false;
1691             }
1692         }
1693 
1694         return true;
1695     }
1696 
1697     /**
1698      * Finishing procedures to be performed after the list has been rebuilt.
1699      * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList.
1700      * @param rebuildCompleted
1701      * @return <code>true</code> if the activity is finishing and creation should halt.
1702      */
1703     protected boolean postRebuildList(boolean rebuildCompleted) {
1704         return postRebuildListInternal(rebuildCompleted);
1705     }
1706 
1707     /**
1708      * Finishing procedures to be performed after the list has been rebuilt.
1709      * @param rebuildCompleted
1710      * @return <code>true</code> if the activity is finishing and creation should halt.
1711      */
1712     final boolean postRebuildListInternal(boolean rebuildCompleted) {
1713         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1714 
1715         // We only rebuild asynchronously when we have multiple elements to sort. In the case where
1716         // we're already done, we can check if we should auto-launch immediately.
1717         if (rebuildCompleted && maybeAutolaunchActivity()) {
1718             return true;
1719         }
1720 
1721         setupViewVisibilities();
1722 
1723         if (shouldShowTabs()) {
1724             setupProfileTabs();
1725         }
1726 
1727         return false;
1728     }
1729 
1730     private int isPermissionGranted(String permission, int uid) {
1731         return ActivityManager.checkComponentPermission(permission, uid,
1732                 /* owningUid= */-1, /* exported= */ true);
1733     }
1734 
1735     /**
1736      * @return {@code true} if a resolved target is autolaunched, otherwise {@code false}
1737      */
1738     private boolean maybeAutolaunchActivity() {
1739         int numberOfProfiles = mMultiProfilePagerAdapter.getItemCount();
1740         if (numberOfProfiles == 1 && maybeAutolaunchIfSingleTarget()) {
1741             return true;
1742         } else if (numberOfProfiles == 2
1743                 && mMultiProfilePagerAdapter.getActiveListAdapter().isTabLoaded()
1744                 && mMultiProfilePagerAdapter.getInactiveListAdapter().isTabLoaded()
1745                 && (maybeAutolaunchIfNoAppsOnInactiveTab()
1746                         || maybeAutolaunchIfCrossProfileSupported())) {
1747             return true;
1748         }
1749         return false;
1750     }
1751 
1752     private boolean maybeAutolaunchIfSingleTarget() {
1753         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1754         if (count != 1) {
1755             return false;
1756         }
1757 
1758         if (mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null) {
1759             return false;
1760         }
1761 
1762         // Only one target, so we're a candidate to auto-launch!
1763         final TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
1764                 .targetInfoForPosition(0, false);
1765         if (shouldAutoLaunchSingleChoice(target)) {
1766             safelyStartActivity(target);
1767             finish();
1768             return true;
1769         }
1770         return false;
1771     }
1772 
1773     private boolean maybeAutolaunchIfNoAppsOnInactiveTab() {
1774         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1775         if (count != 1) {
1776             return false;
1777         }
1778         ResolverListAdapter inactiveListAdapter =
1779                 mMultiProfilePagerAdapter.getInactiveListAdapter();
1780         if (inactiveListAdapter.getUnfilteredCount() != 0) {
1781             return false;
1782         }
1783         TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
1784                 .targetInfoForPosition(0, false);
1785         safelyStartActivity(target);
1786         finish();
1787         return true;
1788     }
1789 
1790     /**
1791      * When we have a personal and a work profile, we auto launch in the following scenario:
1792      * - There is 1 resolved target on each profile
1793      * - That target is the same app on both profiles
1794      * - The target app has permission to communicate cross profiles
1795      * - The target app has declared it supports cross-profile communication via manifest metadata
1796      */
1797     private boolean maybeAutolaunchIfCrossProfileSupported() {
1798         ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
1799         int count = activeListAdapter.getUnfilteredCount();
1800         if (count != 1) {
1801             return false;
1802         }
1803         ResolverListAdapter inactiveListAdapter =
1804                 mMultiProfilePagerAdapter.getInactiveListAdapter();
1805         if (inactiveListAdapter.getUnfilteredCount() != 1) {
1806             return false;
1807         }
1808         TargetInfo activeProfileTarget = activeListAdapter
1809                 .targetInfoForPosition(0, false);
1810         TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
1811         if (!Objects.equals(activeProfileTarget.getResolvedComponentName(),
1812                 inactiveProfileTarget.getResolvedComponentName())) {
1813             return false;
1814         }
1815         if (!shouldAutoLaunchSingleChoice(activeProfileTarget)) {
1816             return false;
1817         }
1818         String packageName = activeProfileTarget.getResolvedComponentName().getPackageName();
1819         if (!canAppInteractCrossProfiles(packageName)) {
1820             return false;
1821         }
1822 
1823         DevicePolicyEventLogger
1824                 .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
1825                 .setBoolean(activeListAdapter.getUserHandle()
1826                         .equals(getPersonalProfileUserHandle()))
1827                 .setStrings(getMetricsCategory())
1828                 .write();
1829         safelyStartActivity(activeProfileTarget);
1830         finish();
1831         return true;
1832     }
1833 
1834     /**
1835      * Returns whether the package has the necessary permissions to interact across profiles on
1836      * behalf of a given user.
1837      *
1838      * <p>This means meeting the following condition:
1839      * <ul>
1840      *     <li>The app's {@link ApplicationInfo#crossProfile} flag must be true, and at least
1841      *     one of the following conditions must be fulfilled</li>
1842      *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.</li>
1843      *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.</li>
1844      *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
1845      *     AppOps {@code android:interact_across_profiles} is set to "allow".</li>
1846      * </ul>
1847      *
1848      */
1849     private boolean canAppInteractCrossProfiles(String packageName) {
1850         ApplicationInfo applicationInfo;
1851         try {
1852             applicationInfo = getPackageManager().getApplicationInfo(packageName, 0);
1853         } catch (NameNotFoundException e) {
1854             Log.e(TAG, "Package " + packageName + " does not exist on current user.");
1855             return false;
1856         }
1857         if (!applicationInfo.crossProfile) {
1858             return false;
1859         }
1860 
1861         int packageUid = applicationInfo.uid;
1862 
1863         if (isPermissionGranted(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
1864                 packageUid) == PackageManager.PERMISSION_GRANTED) {
1865             return true;
1866         }
1867         if (isPermissionGranted(android.Manifest.permission.INTERACT_ACROSS_USERS, packageUid)
1868                 == PackageManager.PERMISSION_GRANTED) {
1869             return true;
1870         }
1871         if (PermissionChecker.checkPermissionForPreflight(this, INTERACT_ACROSS_PROFILES,
1872                 PID_UNKNOWN, packageUid, packageName) == PackageManager.PERMISSION_GRANTED) {
1873             return true;
1874         }
1875         return false;
1876     }
1877 
1878     private boolean isAutolaunching() {
1879         return !mRegistered && isFinishing();
1880     }
1881 
1882     private void setupProfileTabs() {
1883         maybeHideDivider();
1884         TabHost tabHost = findViewById(R.id.profile_tabhost);
1885         tabHost.setup();
1886         ViewPager viewPager = findViewById(R.id.profile_pager);
1887         viewPager.setSaveEnabled(false);
1888 
1889         Button personalButton = (Button) getLayoutInflater().inflate(
1890                 R.layout.resolver_profile_tab_button, tabHost.getTabWidget(), false);
1891         personalButton.setText(getPersonalTabLabel());
1892         personalButton.setContentDescription(getPersonalTabAccessibilityLabel());
1893 
1894         TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL)
1895                 .setContent(R.id.profile_pager)
1896                 .setIndicator(personalButton);
1897         tabHost.addTab(tabSpec);
1898 
1899         Button workButton = (Button) getLayoutInflater().inflate(
1900                 R.layout.resolver_profile_tab_button, tabHost.getTabWidget(), false);
1901         workButton.setText(getWorkTabLabel());
1902         workButton.setContentDescription(getWorkTabAccessibilityLabel());
1903 
1904         tabSpec = tabHost.newTabSpec(TAB_TAG_WORK)
1905                 .setContent(R.id.profile_pager)
1906                 .setIndicator(workButton);
1907         tabHost.addTab(tabSpec);
1908 
1909         TabWidget tabWidget = tabHost.getTabWidget();
1910         tabWidget.setVisibility(View.VISIBLE);
1911         updateActiveTabStyle(tabHost);
1912 
1913         tabHost.setOnTabChangedListener(tabId -> {
1914             updateActiveTabStyle(tabHost);
1915             if (TAB_TAG_PERSONAL.equals(tabId)) {
1916                 viewPager.setCurrentItem(0);
1917             } else {
1918                 viewPager.setCurrentItem(1);
1919             }
1920             setupViewVisibilities();
1921             maybeLogProfileChange();
1922             onProfileTabSelected();
1923             DevicePolicyEventLogger
1924                     .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
1925                     .setInt(viewPager.getCurrentItem())
1926                     .setStrings(getMetricsCategory())
1927                     .write();
1928         });
1929 
1930         viewPager.setVisibility(View.VISIBLE);
1931         tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
1932         mMultiProfilePagerAdapter.setOnProfileSelectedListener(
1933                 new AbstractMultiProfilePagerAdapter.OnProfileSelectedListener() {
1934                     @Override
1935                     public void onProfileSelected(int index) {
1936                         tabHost.setCurrentTab(index);
1937                         resetButtonBar();
1938                         resetCheckedItem();
1939                     }
1940 
1941                     @Override
1942                     public void onProfilePageStateChanged(int state) {
1943                         onHorizontalSwipeStateChanged(state);
1944                     }
1945                 });
1946         mOnSwitchOnWorkSelectedListener = () -> {
1947             final View workTab = tabHost.getTabWidget().getChildAt(1);
1948             workTab.setFocusable(true);
1949             workTab.setFocusableInTouchMode(true);
1950             workTab.requestFocus();
1951         };
1952     }
1953 
1954     private String getPersonalTabLabel() {
1955         return getSystemService(DevicePolicyManager.class).getResources().getString(
1956                 RESOLVER_PERSONAL_TAB, () -> getString(R.string.resolver_personal_tab));
1957     }
1958 
1959     private String getWorkTabLabel() {
1960         return getSystemService(DevicePolicyManager.class).getResources().getString(
1961                 RESOLVER_WORK_TAB, () -> getString(R.string.resolver_work_tab));
1962     }
1963 
1964     void onHorizontalSwipeStateChanged(int state) {}
1965 
1966     private void maybeHideDivider() {
1967         if (!mIsIntentPicker) {
1968             return;
1969         }
1970         final View divider = findViewById(R.id.divider);
1971         if (divider == null) {
1972             return;
1973         }
1974         divider.setVisibility(View.GONE);
1975     }
1976 
1977     /**
1978      * Callback called when user changes the profile tab.
1979      * <p>This method is intended to be overridden by subclasses.
1980      */
1981     protected void onProfileTabSelected() { }
1982 
1983     private void resetCheckedItem() {
1984         if (!mIsIntentPicker) {
1985             return;
1986         }
1987         mLastSelected = ListView.INVALID_POSITION;
1988         ListView inactiveListView = (ListView) mMultiProfilePagerAdapter.getInactiveAdapterView();
1989         if (inactiveListView.getCheckedItemCount() > 0) {
1990             inactiveListView.setItemChecked(inactiveListView.getCheckedItemPosition(), false);
1991         }
1992     }
1993 
1994     private String getPersonalTabAccessibilityLabel() {
1995         return getSystemService(DevicePolicyManager.class).getResources().getString(
1996                 RESOLVER_PERSONAL_TAB_ACCESSIBILITY,
1997                 () -> getString(R.string.resolver_personal_tab_accessibility));
1998     }
1999 
2000     private String getWorkTabAccessibilityLabel() {
2001         return getSystemService(DevicePolicyManager.class).getResources().getString(
2002                 RESOLVER_WORK_TAB_ACCESSIBILITY,
2003                 () -> getString(R.string.resolver_work_tab_accessibility));
2004     }
2005 
2006     private static int getAttrColor(Context context, int attr) {
2007         TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
2008         int colorAccent = ta.getColor(0, 0);
2009         ta.recycle();
2010         return colorAccent;
2011     }
2012 
2013     private void updateActiveTabStyle(TabHost tabHost) {
2014         int currentTab = tabHost.getCurrentTab();
2015         TextView selected = (TextView) tabHost.getTabWidget().getChildAt(currentTab);
2016         TextView unselected = (TextView) tabHost.getTabWidget().getChildAt(1 - currentTab);
2017         selected.setSelected(true);
2018         unselected.setSelected(false);
2019     }
2020 
2021     private void setupViewVisibilities() {
2022         ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
2023         if (!mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)) {
2024             addUseDifferentAppLabelIfNecessary(activeListAdapter);
2025         }
2026     }
2027 
2028     /**
2029      * Add a label to signify that the user can pick a different app.
2030      * @param adapter The adapter used to provide data to item views.
2031      */
2032     public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
2033         final boolean useHeader = adapter.hasFilteredItem();
2034         if (useHeader) {
2035             FrameLayout stub = findViewById(R.id.stub);
2036             stub.setVisibility(View.VISIBLE);
2037             TextView textView = (TextView) LayoutInflater.from(this).inflate(
2038                     R.layout.resolver_different_item_header, null, false);
2039             if (shouldShowTabs()) {
2040                 textView.setGravity(Gravity.CENTER);
2041             }
2042             stub.addView(textView);
2043         }
2044     }
2045 
2046     private void setupAdapterListView(ListView listView, ItemClickListener listener) {
2047         listView.setOnItemClickListener(listener);
2048         listView.setOnItemLongClickListener(listener);
2049 
2050         if (mSupportsAlwaysUseOption) {
2051             listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
2052         }
2053     }
2054 
2055     /**
2056      * Configure the area above the app selection list (title, content preview, etc).
2057      */
2058     private void maybeCreateHeader(ResolverListAdapter listAdapter) {
2059         if (mHeaderCreatorUser != null
2060                 && !listAdapter.getUserHandle().equals(mHeaderCreatorUser)) {
2061             return;
2062         }
2063         if (!shouldShowTabs()
2064                 && listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
2065             final TextView titleView = findViewById(R.id.title);
2066             if (titleView != null) {
2067                 titleView.setVisibility(View.GONE);
2068             }
2069         }
2070 
2071         CharSequence title = mTitle != null
2072                 ? mTitle
2073                 : getTitleForAction(getTargetIntent(), mDefaultTitleResId);
2074 
2075         if (!TextUtils.isEmpty(title)) {
2076             final TextView titleView = findViewById(R.id.title);
2077             if (titleView != null) {
2078                 titleView.setText(title);
2079             }
2080             setTitle(title);
2081         }
2082 
2083         final ImageView iconView = findViewById(R.id.icon);
2084         if (iconView != null) {
2085             listAdapter.loadFilteredItemIconTaskAsync(iconView);
2086         }
2087         mHeaderCreatorUser = listAdapter.getUserHandle();
2088     }
2089 
2090     protected void resetButtonBar() {
2091         if (!mSupportsAlwaysUseOption) {
2092             return;
2093         }
2094         final ViewGroup buttonLayout = findViewById(R.id.button_bar);
2095         if (buttonLayout == null) {
2096             Log.e(TAG, "Layout unexpectedly does not have a button bar");
2097             return;
2098         }
2099         ResolverListAdapter activeListAdapter =
2100                 mMultiProfilePagerAdapter.getActiveListAdapter();
2101         View buttonBarDivider = findViewById(R.id.resolver_button_bar_divider);
2102         if (!useLayoutWithDefault()) {
2103             int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
2104             buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
2105                     buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
2106                             R.dimen.resolver_button_bar_spacing) + inset);
2107         }
2108         if (activeListAdapter.isTabLoaded()
2109                 && mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)
2110                 && !useLayoutWithDefault()) {
2111             buttonLayout.setVisibility(View.INVISIBLE);
2112             if (buttonBarDivider != null) {
2113                 buttonBarDivider.setVisibility(View.INVISIBLE);
2114             }
2115             setButtonBarIgnoreOffset(/* ignoreOffset */ false);
2116             return;
2117         }
2118         if (buttonBarDivider != null) {
2119             buttonBarDivider.setVisibility(View.VISIBLE);
2120         }
2121         buttonLayout.setVisibility(View.VISIBLE);
2122         setButtonBarIgnoreOffset(/* ignoreOffset */ true);
2123 
2124         mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
2125         mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
2126 
2127         resetAlwaysOrOnceButtonBar();
2128     }
2129 
2130     /**
2131      * Updates the button bar container {@code ignoreOffset} layout param.
2132      * <p>Setting this to {@code true} means that the button bar will be glued to the bottom of
2133      * the screen.
2134      */
2135     private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
2136         View buttonBarContainer = findViewById(R.id.button_bar_container);
2137         if (buttonBarContainer != null) {
2138             ResolverDrawerLayout.LayoutParams layoutParams =
2139                     (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
2140             layoutParams.ignoreOffset = ignoreOffset;
2141             buttonBarContainer.setLayoutParams(layoutParams);
2142         }
2143     }
2144 
2145     private void resetAlwaysOrOnceButtonBar() {
2146         // Disable both buttons initially
2147         setAlwaysButtonEnabled(false, ListView.INVALID_POSITION, false);
2148         mOnceButton.setEnabled(false);
2149 
2150         int filteredPosition = mMultiProfilePagerAdapter.getActiveListAdapter()
2151                 .getFilteredPosition();
2152         if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
2153             setAlwaysButtonEnabled(true, filteredPosition, false);
2154             mOnceButton.setEnabled(true);
2155             // Focus the button if we already have the default option
2156             mOnceButton.requestFocus();
2157             return;
2158         }
2159 
2160         // When the items load in, if an item was already selected, enable the buttons
2161         ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
2162         if (currentAdapterView != null
2163                 && currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
2164             setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true);
2165             mOnceButton.setEnabled(true);
2166         }
2167     }
2168 
2169     @Override // ResolverListCommunicator
2170     public boolean useLayoutWithDefault() {
2171         // We only use the default app layout when the profile of the active user has a
2172         // filtered item. We always show the same default app even in the inactive user profile.
2173         boolean currentUserAdapterHasFilteredItem;
2174         if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
2175                 == UserHandle.myUserId()) {
2176             currentUserAdapterHasFilteredItem =
2177                     mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem();
2178         } else {
2179             currentUserAdapterHasFilteredItem =
2180                     mMultiProfilePagerAdapter.getInactiveListAdapter().hasFilteredItem();
2181         }
2182         return mSupportsAlwaysUseOption && currentUserAdapterHasFilteredItem;
2183     }
2184 
2185     /**
2186      * If {@code retainInOnStop} is set to true, we will not finish ourselves when onStop gets
2187      * called and we are launched in a new task.
2188      */
2189     protected void setRetainInOnStop(boolean retainInOnStop) {
2190         mRetainInOnStop = retainInOnStop;
2191     }
2192 
2193     /**
2194      * Check a simple match for the component of two ResolveInfos.
2195      */
2196     @Override // ResolverListCommunicator
2197     public boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
2198         return lhs == null ? rhs == null
2199                 : lhs.activityInfo == null ? rhs.activityInfo == null
2200                 : Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
2201                 && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
2202     }
2203 
2204     protected String getMetricsCategory() {
2205         return METRICS_CATEGORY_RESOLVER;
2206     }
2207 
2208     @Override // ResolverListCommunicator
2209     public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
2210         if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
2211             if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
2212                     && mQuietModeManager.isWaitingToEnableWorkProfile()) {
2213                 // We have just turned on the work profile and entered the pass code to start it,
2214                 // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
2215                 // point in reloading the list now, since the work profile user is still
2216                 // turning on.
2217                 return;
2218             }
2219             boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
2220             if (listRebuilt) {
2221                 ResolverListAdapter activeListAdapter =
2222                         mMultiProfilePagerAdapter.getActiveListAdapter();
2223                 activeListAdapter.notifyDataSetChanged();
2224                 if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) {
2225                     // We no longer have any items...  just finish the activity.
2226                     finish();
2227                 }
2228             }
2229         } else {
2230             mMultiProfilePagerAdapter.clearInactiveProfileCache();
2231         }
2232     }
2233 
2234     private boolean inactiveListAdapterHasItems() {
2235         if (!shouldShowTabs()) {
2236             return false;
2237         }
2238         return mMultiProfilePagerAdapter.getInactiveListAdapter().getCount() > 0;
2239     }
2240 
2241     private BroadcastReceiver createWorkProfileStateReceiver() {
2242         return new BroadcastReceiver() {
2243             @Override
2244             public void onReceive(Context context, Intent intent) {
2245                 String action = intent.getAction();
2246                 if (!TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
2247                         && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
2248                         && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) {
2249                     return;
2250                 }
2251 
2252                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
2253 
2254                 if (userId != getWorkProfileUserHandle().getIdentifier()) {
2255                     return;
2256                 }
2257 
2258                 if (isWorkProfileEnabled()) {
2259                     if (mWorkProfileHasBeenEnabled) {
2260                         return;
2261                     }
2262 
2263                     mWorkProfileHasBeenEnabled = true;
2264                     mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
2265                 } else {
2266                     // Must be an UNAVAILABLE broadcast, so we watch for the next availability
2267                     mWorkProfileHasBeenEnabled = false;
2268                 }
2269 
2270                 if (mMultiProfilePagerAdapter.getCurrentUserHandle()
2271                         .equals(getWorkProfileUserHandle())) {
2272                     mMultiProfilePagerAdapter.rebuildActiveTab(true);
2273                 } else {
2274                     mMultiProfilePagerAdapter.clearInactiveProfileCache();
2275                 }
2276             }
2277         };
2278     }
2279 
2280     public static final class ResolvedComponentInfo {
2281         public final ComponentName name;
2282         private final List<Intent> mIntents = new ArrayList<>();
2283         private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
2284         private boolean mPinned;
2285         private boolean mFixedAtTop;
2286 
2287         public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
2288             this.name = name;
2289             add(intent, info);
2290         }
2291 
2292         public void add(Intent intent, ResolveInfo info) {
2293             mIntents.add(intent);
2294             mResolveInfos.add(info);
2295         }
2296 
2297         public int getCount() {
2298             return mIntents.size();
2299         }
2300 
2301         public Intent getIntentAt(int index) {
2302             return index >= 0 ? mIntents.get(index) : null;
2303         }
2304 
2305         public ResolveInfo getResolveInfoAt(int index) {
2306             return index >= 0 ? mResolveInfos.get(index) : null;
2307         }
2308 
2309         public int findIntent(Intent intent) {
2310             for (int i = 0, N = mIntents.size(); i < N; i++) {
2311                 if (intent.equals(mIntents.get(i))) {
2312                     return i;
2313                 }
2314             }
2315             return -1;
2316         }
2317 
2318         public int findResolveInfo(ResolveInfo info) {
2319             for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
2320                 if (info.equals(mResolveInfos.get(i))) {
2321                     return i;
2322                 }
2323             }
2324             return -1;
2325         }
2326 
2327         public boolean isPinned() {
2328             return mPinned;
2329         }
2330 
2331         public void setPinned(boolean pinned) {
2332             mPinned = pinned;
2333         }
2334 
2335         public boolean isFixedAtTop() {
2336             return mFixedAtTop;
2337         }
2338 
2339         public void setFixedAtTop(boolean isFixedAtTop) {
2340             mFixedAtTop = isFixedAtTop;
2341         }
2342     }
2343 
2344     class ItemClickListener implements AdapterView.OnItemClickListener,
2345             AdapterView.OnItemLongClickListener {
2346         @Override
2347         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
2348             final ListView listView = parent instanceof ListView ? (ListView) parent : null;
2349             if (listView != null) {
2350                 position -= listView.getHeaderViewsCount();
2351             }
2352             if (position < 0) {
2353                 // Header views don't count.
2354                 return;
2355             }
2356             // If we're still loading, we can't yet enable the buttons.
2357             if (mMultiProfilePagerAdapter.getActiveListAdapter()
2358                     .resolveInfoForPosition(position, true) == null) {
2359                 return;
2360             }
2361             ListView currentAdapterView =
2362                     (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
2363             final int checkedPos = currentAdapterView.getCheckedItemPosition();
2364             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
2365             if (!useLayoutWithDefault()
2366                     && (!hasValidSelection || mLastSelected != checkedPos)
2367                     && mAlwaysButton != null) {
2368                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
2369                 mOnceButton.setEnabled(hasValidSelection);
2370                 if (hasValidSelection) {
2371                     currentAdapterView.smoothScrollToPosition(checkedPos);
2372                     mOnceButton.requestFocus();
2373                 }
2374                 mLastSelected = checkedPos;
2375             } else {
2376                 startSelected(position, false, true);
2377             }
2378         }
2379 
2380         @Override
2381         public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
2382             final ListView listView = parent instanceof ListView ? (ListView) parent : null;
2383             if (listView != null) {
2384                 position -= listView.getHeaderViewsCount();
2385             }
2386             if (position < 0) {
2387                 // Header views don't count.
2388                 return false;
2389             }
2390             ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
2391                     .resolveInfoForPosition(position, true);
2392             showTargetDetails(ri);
2393             return true;
2394         }
2395 
2396     }
2397 
2398     static final boolean isSpecificUriMatch(int match) {
2399         match = match&IntentFilter.MATCH_CATEGORY_MASK;
2400         return match >= IntentFilter.MATCH_CATEGORY_HOST
2401                 && match <= IntentFilter.MATCH_CATEGORY_PATH;
2402     }
2403 
2404     static class PickTargetOptionRequest extends PickOptionRequest {
2405         public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
2406                 @Nullable Bundle extras) {
2407             super(prompt, options, extras);
2408         }
2409 
2410         @Override
2411         public void onCancel() {
2412             super.onCancel();
2413             final ResolverActivity ra = (ResolverActivity) getActivity();
2414             if (ra != null) {
2415                 ra.mPickOptionRequest = null;
2416                 ra.finish();
2417             }
2418         }
2419 
2420         @Override
2421         public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
2422             super.onPickOptionResult(finished, selections, result);
2423             if (selections.length != 1) {
2424                 // TODO In a better world we would filter the UI presented here and let the
2425                 // user refine. Maybe later.
2426                 return;
2427             }
2428 
2429             final ResolverActivity ra = (ResolverActivity) getActivity();
2430             if (ra != null) {
2431                 final TargetInfo ti = ra.mMultiProfilePagerAdapter.getActiveListAdapter()
2432                         .getItem(selections[0].getIndex());
2433                 if (ra.onTargetSelected(ti, false)) {
2434                     ra.mPickOptionRequest = null;
2435                     ra.finish();
2436                 }
2437             }
2438         }
2439     }
2440 
2441     protected void maybeLogProfileChange() {}
2442 }
2443