1 /*
2  * Copyright (C) 2019 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.systemui;
18 
19 import android.app.PendingIntent;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 
26 import com.android.systemui.dagger.SysUISingleton;
27 
28 import java.util.List;
29 
30 import javax.inject.Inject;
31 
32 /**
33  * Contains useful methods for querying properties of an Activity Intent.
34  */
35 @SysUISingleton
36 public class ActivityIntentHelper {
37 
38     private final PackageManager mPm;
39 
40     @Inject
ActivityIntentHelper(Context context)41     public ActivityIntentHelper(Context context) {
42         // TODO: inject a package manager, not a context.
43         mPm = context.getPackageManager();
44     }
45 
46     /**
47      * Determines if sending the given intent would result in starting an Intent resolver activity,
48      * instead of resolving to a specific component.
49      *
50      * @param intent the intent
51      * @param currentUserId the id for the user to resolve as
52      * @return true if the intent would launch a resolver activity
53      */
wouldLaunchResolverActivity(Intent intent, int currentUserId)54     public boolean wouldLaunchResolverActivity(Intent intent, int currentUserId) {
55         ActivityInfo targetActivityInfo = getTargetActivityInfo(intent, currentUserId,
56                 false /* onlyDirectBootAware */);
57         return targetActivityInfo == null;
58     }
59 
60     /**
61      * @see #wouldLaunchResolverActivity(Intent, int)
62      */
wouldPendingLaunchResolverActivity(PendingIntent intent, int currentUserId)63     public boolean wouldPendingLaunchResolverActivity(PendingIntent intent, int currentUserId) {
64         ActivityInfo targetActivityInfo = getPendingTargetActivityInfo(intent, currentUserId,
65                 false /* onlyDirectBootAware */);
66         return targetActivityInfo == null;
67     }
68 
69     /**
70      * Returns info about the target Activity of a given intent, or null if the intent does not
71      * resolve to a specific component meeting the requirements.
72      *
73      * @param onlyDirectBootAware a boolean indicating whether the matched activity packages must
74      *         be direct boot aware when in direct boot mode if false, all packages are considered
75      *         a match even if they are not aware.
76      * @return the target activity info of the intent it resolves to a specific package or
77      *         {@code null} if it resolved to the resolver activity
78      */
getTargetActivityInfo(Intent intent, int currentUserId, boolean onlyDirectBootAware)79     public ActivityInfo getTargetActivityInfo(Intent intent, int currentUserId,
80             boolean onlyDirectBootAware) {
81         int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA;
82         if (!onlyDirectBootAware) {
83             flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
84                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
85         }
86         final List<ResolveInfo> appList = mPm.queryIntentActivitiesAsUser(
87                 intent, flags, currentUserId);
88         if (appList.size() == 0) {
89             return null;
90         }
91         if (appList.size() == 1) {
92             return appList.get(0).activityInfo;
93         }
94         ResolveInfo resolved = mPm.resolveActivityAsUser(intent, flags, currentUserId);
95         if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
96             return null;
97         } else {
98             return resolved.activityInfo;
99         }
100     }
101 
102     /**
103      * @see #getTargetActivityInfo(Intent, int, boolean)
104      */
getPendingTargetActivityInfo(PendingIntent intent, int currentUserId, boolean onlyDirectBootAware)105     public ActivityInfo getPendingTargetActivityInfo(PendingIntent intent, int currentUserId,
106             boolean onlyDirectBootAware) {
107         int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA;
108         if (!onlyDirectBootAware) {
109             flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
110                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
111         }
112         final List<ResolveInfo> appList = intent.queryIntentComponents(flags);
113         if (appList.size() == 0) {
114             return null;
115         }
116         if (appList.size() == 1) {
117             return appList.get(0).activityInfo;
118         }
119         ResolveInfo resolved = mPm.resolveActivityAsUser(intent.getIntent(), flags, currentUserId);
120         if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
121             return null;
122         } else {
123             return resolved.activityInfo;
124         }
125     }
126 
127     /**
128      * Determines if the given intent resolves to an Activity which is allowed to appear above
129      * the lock screen.
130      *
131      * @param intent the intent to resolve
132      * @return true if the launched Activity would appear above the lock screen
133      */
wouldShowOverLockscreen(Intent intent, int currentUserId)134     public boolean wouldShowOverLockscreen(Intent intent, int currentUserId) {
135         ActivityInfo targetActivityInfo = getTargetActivityInfo(intent,
136                 currentUserId, false /* onlyDirectBootAware */);
137         return targetActivityInfo != null
138                 && (targetActivityInfo.flags & (ActivityInfo.FLAG_SHOW_WHEN_LOCKED
139                 | ActivityInfo.FLAG_SHOW_FOR_ALL_USERS)) > 0;
140     }
141 
142     /**
143      * @see #wouldShowOverLockscreen(Intent, int)
144      */
wouldPendingShowOverLockscreen(PendingIntent intent, int currentUserId)145     public boolean wouldPendingShowOverLockscreen(PendingIntent intent, int currentUserId) {
146         ActivityInfo targetActivityInfo = getPendingTargetActivityInfo(intent,
147                 currentUserId, false /* onlyDirectBootAware */);
148         return targetActivityInfo != null
149                 && (targetActivityInfo.flags & (ActivityInfo.FLAG_SHOW_WHEN_LOCKED
150                 | ActivityInfo.FLAG_SHOW_FOR_ALL_USERS)) > 0;
151     }
152 
153     /**
154      * Determines if sending the given intent would result in starting an Intent resolver activity,
155      * instead of resolving to a specific component.
156      *
157      * @param resolved the resolveInfo for the intent as returned by resolveActivityAsUser
158      * @param appList a list of resolveInfo as returned by queryIntentActivitiesAsUser
159      * @return true if the intent would launch a resolver activity
160      */
wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList)161     public boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
162         // If the list contains the above resolved activity, then it can't be
163         // ResolverActivity itself.
164         for (int i = 0; i < appList.size(); i++) {
165             ResolveInfo tmp = appList.get(i);
166             if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
167                     && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
168                 return false;
169             }
170         }
171         return true;
172     }
173 }
174