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                     } else {
1214                         // Update Domain Verification status
1215                         ComponentName cn = intent.getComponent();
1216                         String packageName = cn.getPackageName();
1217                         String dataScheme = (data != null) ? data.getScheme() : null;
1218 
1219                         boolean isHttpOrHttps = (dataScheme != null) &&
1220                                 (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
1221                                         dataScheme.equals(IntentFilter.SCHEME_HTTPS));
1222 
1223                         boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
1224                         boolean hasCategoryBrowsable = (categories != null) &&
1225                                 categories.contains(Intent.CATEGORY_BROWSABLE);
1226 
1227                         if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
1228                             pm.updateIntentVerificationStatusAsUser(packageName,
1229                                     PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
1230                                     userId);
1231                         }
1232                     }
1233                 } else {
1234                     try {
1235                         mMultiProfilePagerAdapter.getActiveListAdapter()
1236                                 .mResolverListController.setLastChosen(intent, filter, bestMatch);
1237                     } catch (RemoteException re) {
1238                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
1239                     }
1240                 }
1241             }
1242         }
1243 
1244         if (target != null) {
1245             if (intent != null && isLaunchingTargetInOtherProfile()) {
1246                 prepareIntentForCrossProfileLaunch(intent);
1247             }
1248             safelyStartActivity(target);
1249 
1250             // Rely on the ActivityManager to pop up a dialog regarding app suspension
1251             // and return false
1252             if (target.isSuspended()) {
1253                 return false;
1254             }
1255         }
1256 
1257         return true;
1258     }
1259 
1260     private void prepareIntentForCrossProfileLaunch(Intent intent) {
1261         intent.fixUris(UserHandle.myUserId());
1262     }
1263 
1264     private boolean isLaunchingTargetInOtherProfile() {
1265         return mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
1266                 != UserHandle.myUserId();
1267     }
1268 
1269     @VisibleForTesting
1270     public void safelyStartActivity(TargetInfo cti) {
1271         // We're dispatching intents that might be coming from legacy apps, so
1272         // don't kill ourselves.
1273         StrictMode.disableDeathOnFileUriExposure();
1274         try {
1275             safelyStartActivityInternal(cti);
1276         } finally {
1277             StrictMode.enableDeathOnFileUriExposure();
1278         }
1279     }
1280 
1281     private void safelyStartActivityInternal(TargetInfo cti) {
1282         // If the target is suspended, the activity will not be successfully launched.
1283         // Do not unregister from package manager updates in this case
1284         if (!cti.isSuspended()) {
1285             if (mPersonalPackageMonitor != null) {
1286                 mPersonalPackageMonitor.unregister();
1287             }
1288             if (mWorkPackageMonitor != null) {
1289                 mWorkPackageMonitor.unregister();
1290             }
1291             mRegistered = false;
1292         }
1293         // If needed, show that intent is forwarded
1294         // from managed profile to owner or other way around.
1295         if (mProfileSwitchMessageId != -1) {
1296             Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
1297         }
1298         UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
1299         if (!mSafeForwardingMode) {
1300             if (cti.startAsUser(this, null, currentUserHandle)) {
1301                 onActivityStarted(cti);
1302                 maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
1303             }
1304             return;
1305         }
1306         try {
1307             if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) {
1308                 onActivityStarted(cti);
1309                 maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
1310             }
1311         } catch (RuntimeException e) {
1312             String launchedFromPackage;
1313             try {
1314                 launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
1315                         getActivityToken());
1316             } catch (RemoteException e2) {
1317                 launchedFromPackage = "??";
1318             }
1319             Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
1320                     + " package " + launchedFromPackage + ", while running in "
1321                     + ActivityThread.currentProcessName(), e);
1322         }
1323     }
1324 
1325     private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
1326         if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
1327             return;
1328         }
1329         DevicePolicyEventLogger
1330                 .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
1331                 .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
1332                 .setStrings(getMetricsCategory(),
1333                         cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
1334                 .write();
1335     }
1336 
1337 
1338     public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
1339             int userId) {
1340         // Pass intent to delegate chooser activity with permission token.
1341         // TODO: This should move to a trampoline Activity in the system when the ChooserActivity
1342         // moves into systemui
1343         try {
1344             // TODO: Once this is a small springboard activity, it can move off the UI process
1345             // and we can move the request method to ActivityManagerInternal.
1346             IBinder permissionToken = ActivityTaskManager.getService()
1347                     .requestStartActivityPermissionToken(getActivityToken());
1348             final Intent chooserIntent = new Intent();
1349             final ComponentName delegateActivity = ComponentName.unflattenFromString(
1350                     Resources.getSystem().getString(R.string.config_chooserActivity));
1351             chooserIntent.setClassName(delegateActivity.getPackageName(),
1352                     delegateActivity.getClassName());
1353             chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
1354 
1355             // TODO: These extras will change as chooser activity moves into systemui
1356             chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
1357             chooserIntent.putExtra(ActivityTaskManager.EXTRA_OPTIONS, options);
1358             chooserIntent.putExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY,
1359                     ignoreTargetSecurity);
1360             chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
1361             chooserIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
1362                     | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
1363             startActivity(chooserIntent);
1364         } catch (RemoteException e) {
1365             Log.e(TAG, e.toString());
1366         }
1367         return true;
1368     }
1369 
1370     public void onActivityStarted(TargetInfo cti) {
1371         // Do nothing
1372     }
1373 
1374     @Override // ResolverListCommunicator
1375     public boolean shouldGetActivityMetadata() {
1376         return false;
1377     }
1378 
1379     public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
1380         return !target.isSuspended();
1381     }
1382 
1383     void showTargetDetails(ResolveInfo ri) {
1384         Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
1385                 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
1386                 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
1387         startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
1388     }
1389 
1390     @VisibleForTesting
1391     protected ResolverListAdapter createResolverListAdapter(Context context,
1392             List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
1393             boolean filterLastUsed, UserHandle userHandle) {
1394         Intent startIntent = getIntent();
1395         boolean isAudioCaptureDevice =
1396                 startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
1397         return new ResolverListAdapter(context, payloadIntents, initialIntents, rList,
1398                 filterLastUsed, createListController(userHandle), this,
1399                 isAudioCaptureDevice);
1400     }
1401 
1402     @VisibleForTesting
1403     protected ResolverListController createListController(UserHandle userHandle) {
1404         return new ResolverListController(
1405                 this,
1406                 mPm,
1407                 getTargetIntent(),
1408                 getReferrerPackageName(),
1409                 mLaunchedFromUid,
1410                 userHandle);
1411     }
1412 
1413     /**
1414      * Sets up the content view.
1415      * @return <code>true</code> if the activity is finishing and creation should halt.
1416      */
1417     private boolean configureContentView() {
1418         if (mMultiProfilePagerAdapter.getActiveListAdapter() == null) {
1419             throw new IllegalStateException("mMultiProfilePagerAdapter.getCurrentListAdapter() "
1420                     + "cannot be null.");
1421         }
1422         // We partially rebuild the inactive adapter to determine if we should auto launch
1423         // isTabLoaded will be true here if the empty state screen is shown instead of the list.
1424         boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true)
1425                 || mMultiProfilePagerAdapter.getActiveListAdapter().isTabLoaded();
1426         if (shouldShowTabs()) {
1427             boolean rebuildInactiveCompleted = mMultiProfilePagerAdapter.rebuildInactiveTab(false)
1428                     || mMultiProfilePagerAdapter.getInactiveListAdapter().isTabLoaded();
1429             rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted;
1430         }
1431 
1432         if (useLayoutWithDefault()) {
1433             mLayoutId = R.layout.resolver_list_with_default;
1434         } else {
1435             mLayoutId = getLayoutResource();
1436         }
1437         setContentView(mLayoutId);
1438         mMultiProfilePagerAdapter.setupViewPager(findViewById(R.id.profile_pager));
1439         return postRebuildList(rebuildCompleted);
1440     }
1441 
1442     /**
1443      * Finishing procedures to be performed after the list has been rebuilt.
1444      * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList.
1445      * @param rebuildCompleted
1446      * @return <code>true</code> if the activity is finishing and creation should halt.
1447      */
1448     protected boolean postRebuildList(boolean rebuildCompleted) {
1449         return postRebuildListInternal(rebuildCompleted);
1450     }
1451 
1452     /**
1453      * Finishing procedures to be performed after the list has been rebuilt.
1454      * @param rebuildCompleted
1455      * @return <code>true</code> if the activity is finishing and creation should halt.
1456      */
1457     final boolean postRebuildListInternal(boolean rebuildCompleted) {
1458         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1459 
1460         // We only rebuild asynchronously when we have multiple elements to sort. In the case where
1461         // we're already done, we can check if we should auto-launch immediately.
1462         if (rebuildCompleted && maybeAutolaunchActivity()) {
1463             return true;
1464         }
1465 
1466         setupViewVisibilities();
1467 
1468         if (shouldShowTabs()) {
1469             setupProfileTabs();
1470         }
1471 
1472         return false;
1473     }
1474 
1475     private int isPermissionGranted(String permission, int uid) {
1476         return ActivityManager.checkComponentPermission(permission, uid,
1477                 /* owningUid= */-1, /* exported= */ true);
1478     }
1479 
1480     /**
1481      * @return {@code true} if a resolved target is autolaunched, otherwise {@code false}
1482      */
1483     private boolean maybeAutolaunchActivity() {
1484         int numberOfProfiles = mMultiProfilePagerAdapter.getItemCount();
1485         if (numberOfProfiles == 1 && maybeAutolaunchIfSingleTarget()) {
1486             return true;
1487         } else if (numberOfProfiles == 2
1488                 && mMultiProfilePagerAdapter.getActiveListAdapter().isTabLoaded()
1489                 && mMultiProfilePagerAdapter.getInactiveListAdapter().isTabLoaded()
1490                 && (maybeAutolaunchIfNoAppsOnInactiveTab()
1491                         || maybeAutolaunchIfCrossProfileSupported())) {
1492             return true;
1493         }
1494         return false;
1495     }
1496 
1497     private boolean maybeAutolaunchIfSingleTarget() {
1498         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1499         if (count != 1) {
1500             return false;
1501         }
1502 
1503         if (mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null) {
1504             return false;
1505         }
1506 
1507         // Only one target, so we're a candidate to auto-launch!
1508         final TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
1509                 .targetInfoForPosition(0, false);
1510         if (shouldAutoLaunchSingleChoice(target)) {
1511             safelyStartActivity(target);
1512             finish();
1513             return true;
1514         }
1515         return false;
1516     }
1517 
1518     private boolean maybeAutolaunchIfNoAppsOnInactiveTab() {
1519         int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
1520         if (count != 1) {
1521             return false;
1522         }
1523         ResolverListAdapter inactiveListAdapter =
1524                 mMultiProfilePagerAdapter.getInactiveListAdapter();
1525         if (inactiveListAdapter.getUnfilteredCount() != 0) {
1526             return false;
1527         }
1528         TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
1529                 .targetInfoForPosition(0, false);
1530         safelyStartActivity(target);
1531         finish();
1532         return true;
1533     }
1534 
1535     /**
1536      * When we have a personal and a work profile, we auto launch in the following scenario:
1537      * - There is 1 resolved target on each profile
1538      * - That target is the same app on both profiles
1539      * - The target app has permission to communicate cross profiles
1540      * - The target app has declared it supports cross-profile communication via manifest metadata
1541      */
1542     private boolean maybeAutolaunchIfCrossProfileSupported() {
1543         ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
1544         int count = activeListAdapter.getUnfilteredCount();
1545         if (count != 1) {
1546             return false;
1547         }
1548         ResolverListAdapter inactiveListAdapter =
1549                 mMultiProfilePagerAdapter.getInactiveListAdapter();
1550         if (inactiveListAdapter.getUnfilteredCount() != 1) {
1551             return false;
1552         }
1553         TargetInfo activeProfileTarget = activeListAdapter
1554                 .targetInfoForPosition(0, false);
1555         TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
1556         if (!Objects.equals(activeProfileTarget.getResolvedComponentName(),
1557                 inactiveProfileTarget.getResolvedComponentName())) {
1558             return false;
1559         }
1560         if (!shouldAutoLaunchSingleChoice(activeProfileTarget)) {
1561             return false;
1562         }
1563         String packageName = activeProfileTarget.getResolvedComponentName().getPackageName();
1564         if (!canAppInteractCrossProfiles(packageName)) {
1565             return false;
1566         }
1567 
1568         DevicePolicyEventLogger
1569                 .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
1570                 .setBoolean(activeListAdapter.getUserHandle()
1571                         .equals(getPersonalProfileUserHandle()))
1572                 .setStrings(getMetricsCategory())
1573                 .write();
1574         safelyStartActivity(activeProfileTarget);
1575         finish();
1576         return true;
1577     }
1578 
1579     /**
1580      * Returns whether the package has the necessary permissions to interact across profiles on
1581      * behalf of a given user.
1582      *
1583      * <p>This means meeting the following condition:
1584      * <ul>
1585      *     <li>The app's {@link ApplicationInfo#crossProfile} flag must be true, and at least
1586      *     one of the following conditions must be fulfilled</li>
1587      *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.</li>
1588      *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.</li>
1589      *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
1590      *     AppOps {@code android:interact_across_profiles} is set to "allow".</li>
1591      * </ul>
1592      *
1593      */
1594     private boolean canAppInteractCrossProfiles(String packageName) {
1595         ApplicationInfo applicationInfo;
1596         try {
1597             applicationInfo = getPackageManager().getApplicationInfo(packageName, 0);
1598         } catch (NameNotFoundException e) {
1599             Log.e(TAG, "Package " + packageName + " does not exist on current user.");
1600             return false;
1601         }
1602         if (!applicationInfo.crossProfile) {
1603             return false;
1604         }
1605 
1606         int packageUid = applicationInfo.uid;
1607 
1608         if (isPermissionGranted(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
1609                 packageUid) == PackageManager.PERMISSION_GRANTED) {
1610             return true;
1611         }
1612         if (isPermissionGranted(android.Manifest.permission.INTERACT_ACROSS_USERS, packageUid)
1613                 == PackageManager.PERMISSION_GRANTED) {
1614             return true;
1615         }
1616         if (PermissionChecker.checkPermissionForPreflight(this, INTERACT_ACROSS_PROFILES,
1617                 PID_UNKNOWN, packageUid, packageName) == PackageManager.PERMISSION_GRANTED) {
1618             return true;
1619         }
1620         return false;
1621     }
1622 
1623     private boolean isAutolaunching() {
1624         return !mRegistered && isFinishing();
1625     }
1626 
1627     private void setupProfileTabs() {
1628         maybeHideDivider();
1629         TabHost tabHost = findViewById(R.id.profile_tabhost);
1630         tabHost.setup();
1631         ViewPager viewPager = findViewById(R.id.profile_pager);
1632         viewPager.setSaveEnabled(false);
1633         TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL)
1634                 .setContent(R.id.profile_pager)
1635                 .setIndicator(getString(R.string.resolver_personal_tab));
1636         tabHost.addTab(tabSpec);
1637 
1638         tabSpec = tabHost.newTabSpec(TAB_TAG_WORK)
1639                 .setContent(R.id.profile_pager)
1640                 .setIndicator(getString(R.string.resolver_work_tab));
1641         tabHost.addTab(tabSpec);
1642 
1643         TabWidget tabWidget = tabHost.getTabWidget();
1644         tabWidget.setVisibility(View.VISIBLE);
1645         resetTabsHeaderStyle(tabWidget);
1646         updateActiveTabStyle(tabHost);
1647 
1648         tabHost.setOnTabChangedListener(tabId -> {
1649             resetTabsHeaderStyle(tabWidget);
1650             updateActiveTabStyle(tabHost);
1651             if (TAB_TAG_PERSONAL.equals(tabId)) {
1652                 viewPager.setCurrentItem(0);
1653             } else {
1654                 viewPager.setCurrentItem(1);
1655             }
1656             setupViewVisibilities();
1657             maybeLogProfileChange();
1658             onProfileTabSelected();
1659             DevicePolicyEventLogger
1660                     .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
1661                     .setInt(viewPager.getCurrentItem())
1662                     .setStrings(getMetricsCategory())
1663                     .write();
1664         });
1665 
1666         viewPager.setVisibility(View.VISIBLE);
1667         tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
1668         mMultiProfilePagerAdapter.setOnProfileSelectedListener(
1669                 new AbstractMultiProfilePagerAdapter.OnProfileSelectedListener() {
1670                     @Override
1671                     public void onProfileSelected(int index) {
1672                         tabHost.setCurrentTab(index);
1673                         resetButtonBar();
1674                         resetCheckedItem();
1675                     }
1676 
1677                     @Override
1678                     public void onProfilePageStateChanged(int state) {
1679                         onHorizontalSwipeStateChanged(state);
1680                     }
1681                 });
1682         mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener(
1683                 () -> {
1684                     final View workTab = tabHost.getTabWidget().getChildAt(1);
1685                     workTab.setFocusable(true);
1686                     workTab.setFocusableInTouchMode(true);
1687                     workTab.requestFocus();
1688                 });
1689         findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
1690     }
1691 
1692     void onHorizontalSwipeStateChanged(int state) {}
1693 
1694     private void maybeHideDivider() {
1695         if (!isIntentPicker()) {
1696             return;
1697         }
1698         final View divider = findViewById(R.id.divider);
1699         if (divider == null) {
1700             return;
1701         }
1702         divider.setVisibility(View.GONE);
1703     }
1704 
1705     /**
1706      * Callback called when user changes the profile tab.
1707      * <p>This method is intended to be overridden by subclasses.
1708      */
1709     protected void onProfileTabSelected() { }
1710 
1711     private void resetCheckedItem() {
1712         if (!isIntentPicker()) {
1713             return;
1714         }
1715         mLastSelected = ListView.INVALID_POSITION;
1716         ListView inactiveListView = (ListView) mMultiProfilePagerAdapter.getInactiveAdapterView();
1717         if (inactiveListView.getCheckedItemCount() > 0) {
1718             inactiveListView.setItemChecked(inactiveListView.getCheckedItemPosition(), false);
1719         }
1720     }
1721 
1722     private void resetTabsHeaderStyle(TabWidget tabWidget) {
1723         String workContentDescription = getString(R.string.resolver_work_tab_accessibility);
1724         String personalContentDescription = getString(R.string.resolver_personal_tab_accessibility);
1725         for (int i = 0; i < tabWidget.getChildCount(); i++) {
1726             View tabView = tabWidget.getChildAt(i);
1727             TextView title = tabView.findViewById(android.R.id.title);
1728             title.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_DialogWindowTitle);
1729             title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
1730             title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
1731                     getResources().getDimension(R.dimen.resolver_tab_text_size));
1732             if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
1733                 tabView.setContentDescription(personalContentDescription);
1734             } else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
1735                 tabView.setContentDescription(workContentDescription);
1736             }
1737         }
1738     }
1739 
1740     private static int getAttrColor(Context context, int attr) {
1741         TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
1742         int colorAccent = ta.getColor(0, 0);
1743         ta.recycle();
1744         return colorAccent;
1745     }
1746 
1747     private void updateActiveTabStyle(TabHost tabHost) {
1748         TextView title = tabHost.getTabWidget().getChildAt(tabHost.getCurrentTab())
1749                 .findViewById(android.R.id.title);
1750         title.setTextColor(getAttrColor(this, android.R.attr.colorAccent));
1751     }
1752 
1753     private void setupViewVisibilities() {
1754         ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
1755         if (!mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)) {
1756             addUseDifferentAppLabelIfNecessary(activeListAdapter);
1757         }
1758     }
1759 
1760     /**
1761      * Add a label to signify that the user can pick a different app.
1762      * @param adapter The adapter used to provide data to item views.
1763      */
1764     public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
1765         final boolean useHeader = adapter.hasFilteredItem();
1766         if (useHeader) {
1767             FrameLayout stub = findViewById(R.id.stub);
1768             stub.setVisibility(View.VISIBLE);
1769             TextView textView = (TextView) LayoutInflater.from(this).inflate(
1770                     R.layout.resolver_different_item_header, null, false);
1771             if (shouldShowTabs()) {
1772                 textView.setGravity(Gravity.CENTER);
1773             }
1774             stub.addView(textView);
1775         }
1776     }
1777 
1778     private void setupAdapterListView(ListView listView, ItemClickListener listener) {
1779         listView.setOnItemClickListener(listener);
1780         listView.setOnItemLongClickListener(listener);
1781 
1782         if (mSupportsAlwaysUseOption) {
1783             listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
1784         }
1785     }
1786 
1787     /**
1788      * Configure the area above the app selection list (title, content preview, etc).
1789      */
1790     private void maybeCreateHeader(ResolverListAdapter listAdapter) {
1791         if (mHeaderCreatorUser != null
1792                 && !listAdapter.getUserHandle().equals(mHeaderCreatorUser)) {
1793             return;
1794         }
1795         if (!shouldShowTabs()
1796                 && listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
1797             final TextView titleView = findViewById(R.id.title);
1798             if (titleView != null) {
1799                 titleView.setVisibility(View.GONE);
1800             }
1801         }
1802 
1803         CharSequence title = mTitle != null
1804                 ? mTitle
1805                 : getTitleForAction(getTargetIntent(), mDefaultTitleResId);
1806 
1807         if (!TextUtils.isEmpty(title)) {
1808             final TextView titleView = findViewById(R.id.title);
1809             if (titleView != null) {
1810                 titleView.setText(title);
1811             }
1812             setTitle(title);
1813         }
1814 
1815         final ImageView iconView = findViewById(R.id.icon);
1816         if (iconView != null) {
1817             listAdapter.loadFilteredItemIconTaskAsync(iconView);
1818         }
1819         mHeaderCreatorUser = listAdapter.getUserHandle();
1820     }
1821 
1822     protected void resetButtonBar() {
1823         if (!mSupportsAlwaysUseOption) {
1824             return;
1825         }
1826         final ViewGroup buttonLayout = findViewById(R.id.button_bar);
1827         if (buttonLayout == null) {
1828             Log.e(TAG, "Layout unexpectedly does not have a button bar");
1829             return;
1830         }
1831         ResolverListAdapter activeListAdapter =
1832                 mMultiProfilePagerAdapter.getActiveListAdapter();
1833         View buttonBarDivider = findViewById(R.id.resolver_button_bar_divider);
1834         if (!useLayoutWithDefault()) {
1835             int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
1836             buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
1837                     buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
1838                             R.dimen.resolver_button_bar_spacing) + inset);
1839         }
1840         if (activeListAdapter.isTabLoaded()
1841                 && mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)
1842                 && !useLayoutWithDefault()) {
1843             buttonLayout.setVisibility(View.INVISIBLE);
1844             if (buttonBarDivider != null) {
1845                 buttonBarDivider.setVisibility(View.INVISIBLE);
1846             }
1847             setButtonBarIgnoreOffset(/* ignoreOffset */ false);
1848             return;
1849         }
1850         if (buttonBarDivider != null) {
1851             buttonBarDivider.setVisibility(View.VISIBLE);
1852         }
1853         buttonLayout.setVisibility(View.VISIBLE);
1854         setButtonBarIgnoreOffset(/* ignoreOffset */ true);
1855 
1856         mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
1857         mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
1858 
1859         resetAlwaysOrOnceButtonBar();
1860     }
1861 
1862     /**
1863      * Updates the button bar container {@code ignoreOffset} layout param.
1864      * <p>Setting this to {@code true} means that the button bar will be glued to the bottom of
1865      * the screen.
1866      */
1867     private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
1868         View buttonBarContainer = findViewById(R.id.button_bar_container);
1869         if (buttonBarContainer != null) {
1870             ResolverDrawerLayout.LayoutParams layoutParams =
1871                     (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
1872             layoutParams.ignoreOffset = ignoreOffset;
1873             buttonBarContainer.setLayoutParams(layoutParams);
1874         }
1875     }
1876 
1877     private void resetAlwaysOrOnceButtonBar() {
1878         // Disable both buttons initially
1879         setAlwaysButtonEnabled(false, ListView.INVALID_POSITION, false);
1880         mOnceButton.setEnabled(false);
1881 
1882         int filteredPosition = mMultiProfilePagerAdapter.getActiveListAdapter()
1883                 .getFilteredPosition();
1884         if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
1885             setAlwaysButtonEnabled(true, filteredPosition, false);
1886             mOnceButton.setEnabled(true);
1887             // Focus the button if we already have the default option
1888             mOnceButton.requestFocus();
1889             return;
1890         }
1891 
1892         // When the items load in, if an item was already selected, enable the buttons
1893         ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
1894         if (currentAdapterView != null
1895                 && currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
1896             setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true);
1897             mOnceButton.setEnabled(true);
1898         }
1899     }
1900 
1901     @Override // ResolverListCommunicator
1902     public boolean useLayoutWithDefault() {
1903         // We only use the default app layout when the profile of the active user has a
1904         // filtered item. We always show the same default app even in the inactive user profile.
1905         boolean currentUserAdapterHasFilteredItem;
1906         if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
1907                 == UserHandle.myUserId()) {
1908             currentUserAdapterHasFilteredItem =
1909                     mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem();
1910         } else {
1911             currentUserAdapterHasFilteredItem =
1912                     mMultiProfilePagerAdapter.getInactiveListAdapter().hasFilteredItem();
1913         }
1914         return mSupportsAlwaysUseOption && currentUserAdapterHasFilteredItem;
1915     }
1916 
1917     /**
1918      * If {@code retainInOnStop} is set to true, we will not finish ourselves when onStop gets
1919      * called and we are launched in a new task.
1920      */
1921     protected void setRetainInOnStop(boolean retainInOnStop) {
1922         mRetainInOnStop = retainInOnStop;
1923     }
1924 
1925     /**
1926      * Check a simple match for the component of two ResolveInfos.
1927      */
1928     @Override // ResolverListCommunicator
1929     public boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
1930         return lhs == null ? rhs == null
1931                 : lhs.activityInfo == null ? rhs.activityInfo == null
1932                 : Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
1933                 && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
1934     }
1935 
1936     protected String getMetricsCategory() {
1937         return METRICS_CATEGORY_RESOLVER;
1938     }
1939 
1940     @Override // ResolverListCommunicator
1941     public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
1942         if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
1943             if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
1944                     && mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
1945                 // We have just turned on the work profile and entered the pass code to start it,
1946                 // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
1947                 // point in reloading the list now, since the work profile user is still
1948                 // turning on.
1949                 return;
1950             }
1951             boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
1952             if (listRebuilt) {
1953                 ResolverListAdapter activeListAdapter =
1954                         mMultiProfilePagerAdapter.getActiveListAdapter();
1955                 activeListAdapter.notifyDataSetChanged();
1956                 if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) {
1957                     // We no longer have any items...  just finish the activity.
1958                     finish();
1959                 }
1960             }
1961         } else {
1962             mMultiProfilePagerAdapter.clearInactiveProfileCache();
1963         }
1964     }
1965 
1966     private boolean inactiveListAdapterHasItems() {
1967         if (!shouldShowTabs()) {
1968             return false;
1969         }
1970         return mMultiProfilePagerAdapter.getInactiveListAdapter().getCount() > 0;
1971     }
1972 
1973     private BroadcastReceiver createWorkProfileStateReceiver() {
1974         return new BroadcastReceiver() {
1975             @Override
1976             public void onReceive(Context context, Intent intent) {
1977                 String action = intent.getAction();
1978                 if (!TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
1979                         && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
1980                         && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) {
1981                     return;
1982                 }
1983 
1984                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1985 
1986                 if (userId != getWorkProfileUserHandle().getIdentifier()) {
1987                     return;
1988                 }
1989 
1990                 if (isWorkProfileEnabled()) {
1991                     if (mWorkProfileHasBeenEnabled) {
1992                         return;
1993                     }
1994 
1995                     mWorkProfileHasBeenEnabled = true;
1996                     mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
1997                 } else {
1998                     // Must be an UNAVAILABLE broadcast, so we watch for the next availability
1999                     mWorkProfileHasBeenEnabled = false;
2000                 }
2001 
2002                 if (mMultiProfilePagerAdapter.getCurrentUserHandle()
2003                         .equals(getWorkProfileUserHandle())) {
2004                     mMultiProfilePagerAdapter.rebuildActiveTab(true);
2005                 } else {
2006                     mMultiProfilePagerAdapter.clearInactiveProfileCache();
2007                 }
2008             }
2009         };
2010     }
2011 
2012     @VisibleForTesting
2013     public static final class ResolvedComponentInfo {
2014         public final ComponentName name;
2015         private final List<Intent> mIntents = new ArrayList<>();
2016         private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
2017         private boolean mPinned;
2018 
2019         public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
2020             this.name = name;
2021             add(intent, info);
2022         }
2023 
2024         public void add(Intent intent, ResolveInfo info) {
2025             mIntents.add(intent);
2026             mResolveInfos.add(info);
2027         }
2028 
2029         public int getCount() {
2030             return mIntents.size();
2031         }
2032 
2033         public Intent getIntentAt(int index) {
2034             return index >= 0 ? mIntents.get(index) : null;
2035         }
2036 
2037         public ResolveInfo getResolveInfoAt(int index) {
2038             return index >= 0 ? mResolveInfos.get(index) : null;
2039         }
2040 
2041         public int findIntent(Intent intent) {
2042             for (int i = 0, N = mIntents.size(); i < N; i++) {
2043                 if (intent.equals(mIntents.get(i))) {
2044                     return i;
2045                 }
2046             }
2047             return -1;
2048         }
2049 
2050         public int findResolveInfo(ResolveInfo info) {
2051             for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
2052                 if (info.equals(mResolveInfos.get(i))) {
2053                     return i;
2054                 }
2055             }
2056             return -1;
2057         }
2058 
2059         public boolean isPinned() {
2060             return mPinned;
2061         }
2062 
2063         public void setPinned(boolean pinned) {
2064             mPinned = pinned;
2065         }
2066     }
2067 
2068     class ItemClickListener implements AdapterView.OnItemClickListener,
2069             AdapterView.OnItemLongClickListener {
2070         @Override
2071         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
2072             final ListView listView = parent instanceof ListView ? (ListView) parent : null;
2073             if (listView != null) {
2074                 position -= listView.getHeaderViewsCount();
2075             }
2076             if (position < 0) {
2077                 // Header views don't count.
2078                 return;
2079             }
2080             // If we're still loading, we can't yet enable the buttons.
2081             if (mMultiProfilePagerAdapter.getActiveListAdapter()
2082                     .resolveInfoForPosition(position, true) == null) {
2083                 return;
2084             }
2085             ListView currentAdapterView =
2086                     (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
2087             final int checkedPos = currentAdapterView.getCheckedItemPosition();
2088             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
2089             if (!useLayoutWithDefault()
2090                     && (!hasValidSelection || mLastSelected != checkedPos)
2091                     && mAlwaysButton != null) {
2092                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
2093                 mOnceButton.setEnabled(hasValidSelection);
2094                 if (hasValidSelection) {
2095                     currentAdapterView.smoothScrollToPosition(checkedPos);
2096                     mOnceButton.requestFocus();
2097                 }
2098                 mLastSelected = checkedPos;
2099             } else {
2100                 startSelected(position, false, true);
2101             }
2102         }
2103 
2104         @Override
2105         public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
2106             final ListView listView = parent instanceof ListView ? (ListView) parent : null;
2107             if (listView != null) {
2108                 position -= listView.getHeaderViewsCount();
2109             }
2110             if (position < 0) {
2111                 // Header views don't count.
2112                 return false;
2113             }
2114             ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
2115                     .resolveInfoForPosition(position, true);
2116             showTargetDetails(ri);
2117             return true;
2118         }
2119 
2120     }
2121 
2122     static final boolean isSpecificUriMatch(int match) {
2123         match = match&IntentFilter.MATCH_CATEGORY_MASK;
2124         return match >= IntentFilter.MATCH_CATEGORY_HOST
2125                 && match <= IntentFilter.MATCH_CATEGORY_PATH;
2126     }
2127 
2128     static class PickTargetOptionRequest extends PickOptionRequest {
2129         public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
2130                 @Nullable Bundle extras) {
2131             super(prompt, options, extras);
2132         }
2133 
2134         @Override
2135         public void onCancel() {
2136             super.onCancel();
2137             final ResolverActivity ra = (ResolverActivity) getActivity();
2138             if (ra != null) {
2139                 ra.mPickOptionRequest = null;
2140                 ra.finish();
2141             }
2142         }
2143 
2144         @Override
2145         public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
2146             super.onPickOptionResult(finished, selections, result);
2147             if (selections.length != 1) {
2148                 // TODO In a better world we would filter the UI presented here and let the
2149                 // user refine. Maybe later.
2150                 return;
2151             }
2152 
2153             final ResolverActivity ra = (ResolverActivity) getActivity();
2154             if (ra != null) {
2155                 final TargetInfo ti = ra.mMultiProfilePagerAdapter.getActiveListAdapter()
2156                         .getItem(selections[0].getIndex());
2157                 if (ra.onTargetSelected(ti, false)) {
2158                     ra.mPickOptionRequest = null;
2159                     ra.finish();
2160                 }
2161             }
2162         }
2163     }
2164 
2165     protected void maybeLogProfileChange() {}
2166 }
2167