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 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.permissioncontroller.PermissionControllerStatsLog; 36 import com.android.permissioncontroller.permission.utils.CollectionUtils; 37 import com.android.permissioncontroller.role.model.Role; 38 import com.android.permissioncontroller.role.model.Roles; 39 import com.android.permissioncontroller.role.model.UserDeniedManager; 40 import com.android.permissioncontroller.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