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