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