/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.permissioncontroller.role.ui; import static com.android.permissioncontroller.Constants.EXTRA_IS_ECM_IN_APP; import android.app.role.RoleManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.os.Bundle; import android.os.Process; import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import com.android.permissioncontroller.DeviceUtils; import com.android.permissioncontroller.PermissionControllerStatsLog; import com.android.permissioncontroller.permission.utils.CollectionUtils; import com.android.permissioncontroller.role.model.UserDeniedManager; import com.android.permissioncontroller.role.ui.wear.WearRequestRoleFragment; import com.android.permissioncontroller.role.utils.PackageUtils; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; import java.util.List; import java.util.Objects; /** * {@code Activity} for a role request. */ public class RequestRoleActivity extends FragmentActivity { private static final String LOG_TAG = RequestRoleActivity.class.getSimpleName(); private String mRoleName; private String mPackageName; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addSystemFlags( WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); mRoleName = getIntent().getStringExtra(Intent.EXTRA_ROLE_NAME); mPackageName = getCallingPackage(); if (!handleChangeDefaultDialerDialogCompatibility()) { reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (!handleSmsDefaultDialogCompatibility()) { reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (TextUtils.isEmpty(mRoleName)) { Log.w(LOG_TAG, "Role name cannot be null or empty: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (TextUtils.isEmpty(mPackageName)) { Log.w(LOG_TAG, "Package name cannot be null or empty: " + mPackageName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } // Perform checks here so that we have a chance to finish without being visible to user. Role role = Roles.get(this).get(mRoleName); if (role == null) { Log.w(LOG_TAG, "Unknown role: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (!role.isAvailableAsUser(Process.myUserHandle(), this)) { Log.e(LOG_TAG, "Role is unavailable: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (!role.isVisibleAsUser(Process.myUserHandle(), this)) { Log.e(LOG_TAG, "Role is invisible: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (!role.isRequestable()) { Log.e(LOG_TAG, "Role is not requestable: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } if (!role.isExclusive()) { Log.e(LOG_TAG, "Role is not exclusive: " + mRoleName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(mPackageName, this); if (applicationInfo == null) { Log.w(LOG_TAG, "Unknown application: " + mPackageName); reportRequestResult( PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED); finish(); return; } RoleManager roleManager = getSystemService(RoleManager.class); List currentPackageNames = roleManager.getRoleHolders(mRoleName); if (currentPackageNames.contains(mPackageName)) { Log.i(LOG_TAG, "Application is already a role holder, role: " + mRoleName + ", package: " + mPackageName); reportRequestResult(PermissionControllerStatsLog .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ALREADY_GRANTED); setResult(RESULT_OK); finish(); return; } Intent restrictionIntent = role.getApplicationRestrictionIntentAsUser(applicationInfo, Process.myUserHandle(), this); if (restrictionIntent != null) { if (Objects.equals(restrictionIntent.getAction(), Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)) { Log.w(LOG_TAG, "Cannot request role due to user restriction" + ", role: " + mRoleName + ", package: " + mPackageName); reportRequestResult(PermissionControllerStatsLog .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_RESTRICTION); } else { Log.w(LOG_TAG, "Cannot request role due to enhanced confirmation restriction" + ", role: " + mRoleName + ", package: " + mPackageName); reportRequestResult(PermissionControllerStatsLog .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ENHANCED_CONFIRMATION_RESTRICTION); restrictionIntent.putExtra(EXTRA_IS_ECM_IN_APP, true); } startActivity(restrictionIntent); finish(); return; } if (!role.isPackageQualifiedAsUser(mPackageName, Process.myUserHandle(), this)) { Log.w(LOG_TAG, "Application doesn't qualify for role, role: " + mRoleName + ", package: " + mPackageName); reportRequestResult(PermissionControllerStatsLog .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_NOT_QUALIFIED); finish(); return; } if (UserDeniedManager.getInstance(this).isDeniedAlways(mRoleName, mPackageName)) { Log.w(LOG_TAG, "Application is denied always for role, role: " + mRoleName + ", package: " + mPackageName); reportRequestResult(PermissionControllerStatsLog .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_ALWAYS_DENIED); finish(); return; } if (savedInstanceState == null) { if (DeviceUtils.isWear(this)) { WearRequestRoleFragment fragment = WearRequestRoleFragment.newInstance( mRoleName, mPackageName); getSupportFragmentManager().beginTransaction() .add(android.R.id.content, fragment) .commit(); } else { RequestRoleFragment fragment = RequestRoleFragment.newInstance(mRoleName, mPackageName); getSupportFragmentManager().beginTransaction() .add(fragment, null) .commit(); } } } /** * Handle compatibility with the old * {@link com.android.server.telecom.components.ChangeDefaultDialerDialog}. * * @return whether we should continue requesting the role. The activity should be finished if * {@code false} is returned. */ private boolean handleChangeDefaultDialerDialogCompatibility() { Intent intent = getIntent(); if (!Objects.equals(intent.getAction(), TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)) { return true; } Log.w(LOG_TAG, "TelecomManager.ACTION_CHANGE_DEFAULT_DIALER is deprecated; please use" + " RoleManager.createRequestRoleIntent() and Activity.startActivityForResult()" + " instead"); mRoleName = RoleManager.ROLE_DIALER; mPackageName = null; // Intent.EXTRA_CALLING_PACKAGE is set in PermissionPolicyService.Internal // .isActionRemovedForCallingPackage() and can be trusted. String callingPackageName = intent.getStringExtra(Intent.EXTRA_CALLING_PACKAGE); String extraPackageName = intent.getStringExtra( TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME); if (Objects.equals(extraPackageName, callingPackageName)) { // Requesting for itself is okay. mPackageName = extraPackageName; return true; } RoleManager roleManager = getSystemService(RoleManager.class); String holderPackageName = CollectionUtils.firstOrNull(roleManager.getRoleHolders( RoleManager.ROLE_DIALER)); if (Objects.equals(callingPackageName, holderPackageName)) { // Giving away its own role is okay. mPackageName = extraPackageName; return true; } // If we reach here it's not okay. return false; } /** * Handle compatibility with the old {@link com.android.settings.SmsDefaultDialog}. * * @return whether we should continue requesting the role. The activity should be finished if * {@code false} is returned. */ private boolean handleSmsDefaultDialogCompatibility() { Intent intent = getIntent(); if (!Objects.equals(intent.getAction(), Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)) { return true; } Log.w(LOG_TAG, "Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT is deprecated; please use" + " RoleManager.createRequestRoleIntent() and Activity.startActivityForResult()" + " instead"); mRoleName = RoleManager.ROLE_SMS; mPackageName = null; // Intent.EXTRA_CALLING_PACKAGE is set in PermissionPolicyService.Internal // .isActionRemovedForCallingPackage() and can be trusted. String callingPackageName = intent.getStringExtra(Intent.EXTRA_CALLING_PACKAGE); String extraPackageName = intent.getStringExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME); if (extraPackageName == null) { // Launch the settings activity to show the list. // TODO: Return RESULT_OK if any changes were made? Intent defaultAppActivityIntent = DefaultAppActivity.createIntent( RoleManager.ROLE_SMS, Process.myUserHandle(), this) .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); startActivity(defaultAppActivityIntent); return false; } if (Objects.equals(extraPackageName, callingPackageName)) { // Requesting for itself is okay. mPackageName = extraPackageName; return true; } RoleManager roleManager = getSystemService(RoleManager.class); String holderPackageName = CollectionUtils.firstOrNull(roleManager.getRoleHolders( RoleManager.ROLE_SMS)); if (Objects.equals(callingPackageName, holderPackageName)) { // Giving away its own role is okay. mPackageName = extraPackageName; return true; } // If we reach here it's not okay. return false; } private void reportRequestResult(int result) { RequestRoleFragment.reportRequestResult(getApplicationUid(mPackageName, this), mPackageName, mRoleName, -1, -1, null, -1, null, result); } private static int getApplicationUid(@Nullable String packageName, @NonNull Context context) { if (packageName == null) { return -1; } ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, context); if (applicationInfo == null) { return -1; } return applicationInfo.uid; } @Override protected void onNewIntent(@NonNull Intent intent) { super.onNewIntent(intent); Log.w(LOG_TAG, "Ignoring new intent: " + intent); } }