blob: 279e55266eb97e318068de5ad146af3d29be0d01 [file] [log] [blame]
/*
* 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 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.os.UserManager;
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;
}
if (PackageUtils.getApplicationInfo(mPackageName, this) == null) {
Log.w(LOG_TAG, "Unknown application: " + mPackageName);
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
finish();
return;
}
RoleManager roleManager = getSystemService(RoleManager.class);
List<String> 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;
}
UserManager userManager = getSystemService(UserManager.class);
if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DEFAULT_APPS)) {
Log.w(LOG_TAG, "Cannot request role due to user restriction"
+ " DISALLOW_CONFIG_DEFAULT_APPS, role: " + mRoleName);
reportRequestResult(PermissionControllerStatsLog
.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_RESTRICTION);
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);
}
}