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