1 /*
2  * Copyright (C) 2018 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.permissioncontroller.role.ui;
18 
19 import static com.android.permissioncontroller.Constants.EXTRA_IS_ECM_IN_APP;
20 
21 import android.app.role.RoleManager;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.os.Bundle;
26 import android.os.Process;
27 import android.provider.Settings;
28 import android.provider.Telephony;
29 import android.telecom.TelecomManager;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.WindowManager;
33 
34 import androidx.annotation.NonNull;
35 import androidx.annotation.Nullable;
36 import androidx.fragment.app.FragmentActivity;
37 
38 import com.android.permissioncontroller.DeviceUtils;
39 import com.android.permissioncontroller.PermissionControllerStatsLog;
40 import com.android.permissioncontroller.permission.utils.CollectionUtils;
41 import com.android.permissioncontroller.role.model.UserDeniedManager;
42 import com.android.permissioncontroller.role.ui.wear.WearRequestRoleFragment;
43 import com.android.permissioncontroller.role.utils.PackageUtils;
44 import com.android.role.controller.model.Role;
45 import com.android.role.controller.model.Roles;
46 
47 import java.util.List;
48 import java.util.Objects;
49 
50 /**
51  * {@code Activity} for a role request.
52  */
53 public class RequestRoleActivity extends FragmentActivity {
54 
55     private static final String LOG_TAG = RequestRoleActivity.class.getSimpleName();
56 
57     private String mRoleName;
58     private String mPackageName;
59 
60     @Override
onCreate(@ullable Bundle savedInstanceState)61     protected void onCreate(@Nullable Bundle savedInstanceState) {
62         super.onCreate(savedInstanceState);
63 
64         getWindow().addSystemFlags(
65                 WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
66 
67         mRoleName = getIntent().getStringExtra(Intent.EXTRA_ROLE_NAME);
68         mPackageName = getCallingPackage();
69 
70         if (!handleChangeDefaultDialerDialogCompatibility()) {
71             reportRequestResult(
72                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
73             finish();
74             return;
75         }
76 
77         if (!handleSmsDefaultDialogCompatibility()) {
78             reportRequestResult(
79                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
80             finish();
81             return;
82         }
83 
84         if (TextUtils.isEmpty(mRoleName)) {
85             Log.w(LOG_TAG, "Role name cannot be null or empty: " + mRoleName);
86             reportRequestResult(
87                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
88             finish();
89             return;
90         }
91         if (TextUtils.isEmpty(mPackageName)) {
92             Log.w(LOG_TAG, "Package name cannot be null or empty: " + mPackageName);
93             reportRequestResult(
94                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
95             finish();
96             return;
97         }
98 
99         // Perform checks here so that we have a chance to finish without being visible to user.
100         Role role = Roles.get(this).get(mRoleName);
101         if (role == null) {
102             Log.w(LOG_TAG, "Unknown role: " + mRoleName);
103             reportRequestResult(
104                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
105             finish();
106             return;
107         }
108 
109         if (!role.isAvailableAsUser(Process.myUserHandle(), this)) {
110             Log.e(LOG_TAG, "Role is unavailable: " + mRoleName);
111             reportRequestResult(
112                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
113             finish();
114             return;
115         }
116 
117         if (!role.isVisibleAsUser(Process.myUserHandle(), this)) {
118             Log.e(LOG_TAG, "Role is invisible: " + mRoleName);
119             reportRequestResult(
120                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
121             finish();
122             return;
123         }
124 
125         if (!role.isRequestable()) {
126             Log.e(LOG_TAG, "Role is not requestable: " + mRoleName);
127             reportRequestResult(
128                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
129             finish();
130             return;
131         }
132 
133         if (!role.isExclusive()) {
134             Log.e(LOG_TAG, "Role is not exclusive: " + mRoleName);
135             reportRequestResult(
136                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
137             finish();
138             return;
139         }
140 
141         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(mPackageName, this);
142         if (applicationInfo == null) {
143             Log.w(LOG_TAG, "Unknown application: " + mPackageName);
144             reportRequestResult(
145                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
146             finish();
147             return;
148         }
149 
150         RoleManager roleManager = getSystemService(RoleManager.class);
151         List<String> currentPackageNames = roleManager.getRoleHolders(mRoleName);
152         if (currentPackageNames.contains(mPackageName)) {
153             Log.i(LOG_TAG, "Application is already a role holder, role: " + mRoleName
154                     + ", package: " + mPackageName);
155             reportRequestResult(PermissionControllerStatsLog
156                     .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ALREADY_GRANTED);
157             setResult(RESULT_OK);
158             finish();
159             return;
160         }
161 
162         Intent restrictionIntent = role.getApplicationRestrictionIntentAsUser(applicationInfo,
163                 Process.myUserHandle(), this);
164         if (restrictionIntent != null) {
165             if (Objects.equals(restrictionIntent.getAction(),
166                     Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)) {
167                 Log.w(LOG_TAG, "Cannot request role due to user restriction"
168                         + ", role: " + mRoleName + ", package: " + mPackageName);
169                 reportRequestResult(PermissionControllerStatsLog
170                         .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_RESTRICTION);
171             } else {
172                 Log.w(LOG_TAG, "Cannot request role due to enhanced confirmation restriction"
173                         + ", role: " + mRoleName + ", package: " + mPackageName);
174                 reportRequestResult(PermissionControllerStatsLog
175                         .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ENHANCED_CONFIRMATION_RESTRICTION);
176                 restrictionIntent.putExtra(EXTRA_IS_ECM_IN_APP, true);
177             }
178             startActivity(restrictionIntent);
179             finish();
180             return;
181         }
182 
183         if (!role.isPackageQualifiedAsUser(mPackageName, Process.myUserHandle(), this)) {
184             Log.w(LOG_TAG, "Application doesn't qualify for role, role: " + mRoleName
185                     + ", package: " + mPackageName);
186             reportRequestResult(PermissionControllerStatsLog
187                     .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_NOT_QUALIFIED);
188             finish();
189             return;
190         }
191 
192         if (UserDeniedManager.getInstance(this).isDeniedAlways(mRoleName, mPackageName)) {
193             Log.w(LOG_TAG, "Application is denied always for role, role: " + mRoleName
194                     + ", package: " + mPackageName);
195             reportRequestResult(PermissionControllerStatsLog
196                     .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_ALWAYS_DENIED);
197             finish();
198             return;
199         }
200 
201         if (savedInstanceState == null) {
202             if (DeviceUtils.isWear(this)) {
203                 WearRequestRoleFragment fragment = WearRequestRoleFragment.newInstance(
204                         mRoleName, mPackageName);
205                 getSupportFragmentManager().beginTransaction()
206                         .add(android.R.id.content, fragment)
207                         .commit();
208             } else {
209                 RequestRoleFragment fragment = RequestRoleFragment.newInstance(mRoleName,
210                         mPackageName);
211                 getSupportFragmentManager().beginTransaction()
212                         .add(fragment, null)
213                         .commit();
214             }
215         }
216     }
217 
218     /**
219      * Handle compatibility with the old
220      * {@link com.android.server.telecom.components.ChangeDefaultDialerDialog}.
221      *
222      * @return whether we should continue requesting the role. The activity should be finished if
223      *         {@code false} is returned.
224      */
handleChangeDefaultDialerDialogCompatibility()225     private boolean handleChangeDefaultDialerDialogCompatibility() {
226         Intent intent = getIntent();
227         if (!Objects.equals(intent.getAction(), TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)) {
228             return true;
229         }
230 
231         Log.w(LOG_TAG, "TelecomManager.ACTION_CHANGE_DEFAULT_DIALER is deprecated; please use"
232                 + " RoleManager.createRequestRoleIntent() and Activity.startActivityForResult()"
233                 + " instead");
234 
235         mRoleName = RoleManager.ROLE_DIALER;
236         mPackageName = null;
237 
238         // Intent.EXTRA_CALLING_PACKAGE is set in PermissionPolicyService.Internal
239         // .isActionRemovedForCallingPackage() and can be trusted.
240         String callingPackageName = intent.getStringExtra(Intent.EXTRA_CALLING_PACKAGE);
241         String extraPackageName = intent.getStringExtra(
242                 TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
243         if (Objects.equals(extraPackageName, callingPackageName)) {
244             // Requesting for itself is okay.
245             mPackageName = extraPackageName;
246             return true;
247         }
248 
249         RoleManager roleManager = getSystemService(RoleManager.class);
250         String holderPackageName = CollectionUtils.firstOrNull(roleManager.getRoleHolders(
251                 RoleManager.ROLE_DIALER));
252         if (Objects.equals(callingPackageName, holderPackageName)) {
253             // Giving away its own role is okay.
254             mPackageName = extraPackageName;
255             return true;
256         }
257 
258         // If we reach here it's not okay.
259         return false;
260     }
261 
262     /**
263      * Handle compatibility with the old {@link com.android.settings.SmsDefaultDialog}.
264      *
265      * @return whether we should continue requesting the role. The activity should be finished if
266      *         {@code false} is returned.
267      */
handleSmsDefaultDialogCompatibility()268     private boolean handleSmsDefaultDialogCompatibility() {
269         Intent intent = getIntent();
270         if (!Objects.equals(intent.getAction(), Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)) {
271             return true;
272         }
273 
274         Log.w(LOG_TAG, "Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT is deprecated; please use"
275                 + " RoleManager.createRequestRoleIntent() and Activity.startActivityForResult()"
276                 + " instead");
277 
278         mRoleName = RoleManager.ROLE_SMS;
279         mPackageName = null;
280 
281         // Intent.EXTRA_CALLING_PACKAGE is set in PermissionPolicyService.Internal
282         // .isActionRemovedForCallingPackage() and can be trusted.
283         String callingPackageName = intent.getStringExtra(Intent.EXTRA_CALLING_PACKAGE);
284         String extraPackageName = intent.getStringExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME);
285         if (extraPackageName == null) {
286             // Launch the settings activity to show the list.
287             // TODO: Return RESULT_OK if any changes were made?
288             Intent defaultAppActivityIntent = DefaultAppActivity.createIntent(
289                     RoleManager.ROLE_SMS, Process.myUserHandle(), this)
290                     .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
291             startActivity(defaultAppActivityIntent);
292             return false;
293         }
294 
295         if (Objects.equals(extraPackageName, callingPackageName)) {
296             // Requesting for itself is okay.
297             mPackageName = extraPackageName;
298             return true;
299         }
300 
301         RoleManager roleManager = getSystemService(RoleManager.class);
302         String holderPackageName = CollectionUtils.firstOrNull(roleManager.getRoleHolders(
303                 RoleManager.ROLE_SMS));
304         if (Objects.equals(callingPackageName, holderPackageName)) {
305             // Giving away its own role is okay.
306             mPackageName = extraPackageName;
307             return true;
308         }
309 
310         // If we reach here it's not okay.
311         return false;
312     }
313 
reportRequestResult(int result)314     private void reportRequestResult(int result) {
315         RequestRoleFragment.reportRequestResult(getApplicationUid(mPackageName, this), mPackageName,
316                 mRoleName, -1, -1, null, -1, null, result);
317     }
318 
getApplicationUid(@ullable String packageName, @NonNull Context context)319     private static int getApplicationUid(@Nullable String packageName, @NonNull Context context) {
320         if (packageName == null) {
321             return -1;
322         }
323         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context);
324         if (applicationInfo == null) {
325             return -1;
326         }
327         return applicationInfo.uid;
328     }
329 
330     @Override
onNewIntent(@onNull Intent intent)331     protected void onNewIntent(@NonNull Intent intent) {
332         super.onNewIntent(intent);
333 
334         Log.w(LOG_TAG, "Ignoring new intent: " + intent);
335     }
336 }
337