blob: a0c9676810b33236666e16478eaa19f54401b61f [file] [log] [blame]
/*
* Copyright (C) 2017 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.car.settings.applications;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.icu.text.ListFormatter;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.car.settings.CarSettingActivity;
import com.android.car.settings.R;
import com.android.settingslib.Utils;
import com.android.settingslib.applications.PermissionsSummaryHelper;
import com.android.settingslib.applications.PermissionsSummaryHelper.PermissionsResultCallback;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
/**
* Shows details about an application and action associated with that application,
* like uninstall, forceStop.
*/
public class ApplicationDetailActivity extends CarSettingActivity {
private static final String TAG = "AppDetailActivity";
public static final String APPLICATION_INFO_KEY = "APPLICATION_INFO_KEY";
private ResolveInfo mResolveInfo;
private TextView mPermissionDetailView;
private View mPermissionContainer;
private Button mDisableToggle;
private Button mForceStopButton;
private DevicePolicyManager mDpm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showMenuIcon();
setContentView(R.layout.application_details);
if (getIntent() != null && getIntent().getExtras() != null) {
mResolveInfo = getIntent().getExtras().getParcelable(APPLICATION_INFO_KEY);
}
if (mResolveInfo == null) {
Log.w(TAG, "No application info set.");
return;
}
mDisableToggle = (Button) findViewById(R.id.disable_toggle);
mForceStopButton = (Button) findViewById(R.id.force_stop);
mPermissionDetailView = (TextView) findViewById(R.id.permission_detail);
mPermissionContainer = findViewById(R.id.permission_container);
ImageView icon = (ImageView) findViewById(R.id.icon);
icon.setImageDrawable(mResolveInfo.loadIcon(getPackageManager()));
TextView appName = (TextView) findViewById(R.id.name);
appName.setText(mResolveInfo.loadLabel(getPackageManager()));
PermissionsSummaryHelper.getPermissionSummary(this /* context */,
mResolveInfo.activityInfo.packageName, mPermissionCallback);
mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
updateForceStopButton();
mForceStopButton.setOnClickListener(v -> {
forceStopPackage(mResolveInfo.activityInfo.packageName);
});
updateDisableable();
}
// fetch the latest ApplicationInfo instead of caching it so it reflects the current state.
private ApplicationInfo getAppInfo() {
try {
return getPackageManager().getApplicationInfo(
mResolveInfo.activityInfo.packageName, 0 /* flag */);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "incorrect packagename: " + mResolveInfo.activityInfo.packageName, e);
throw new IllegalArgumentException(e);
}
}
private PackageInfo getPackageInfo() {
try {
return getPackageManager().getPackageInfo(
mResolveInfo.activityInfo.packageName, 0 /* flag */);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "incorrect packagename: " + mResolveInfo.activityInfo.packageName, e);
throw new IllegalArgumentException(e);
}
}
private void updateDisableable() {
boolean disableable = false;
boolean disabled = false;
// Try to prevent the user from bricking their phone
// by not allowing disabling of apps in the system.
if (Utils.isSystemPackage(getResources(), getPackageManager(), getPackageInfo())) {
// Disable button for core system applications.
mDisableToggle.setText(R.string.disable_text);
disabled = false;
} else if (getAppInfo().enabled && !isDisabledUntilUsed()) {
mDisableToggle.setText(R.string.disable_text);
disableable = true;
disabled = false;
} else {
mDisableToggle.setText(R.string.enable_text);
disableable = true;
disabled = true;
}
mDisableToggle.setEnabled(disableable);
final int enableState = disabled
? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
mDisableToggle.setOnClickListener(v -> {
getPackageManager().setApplicationEnabledSetting(
mResolveInfo.activityInfo.packageName,
enableState,
0);
updateDisableable();
});
}
private boolean isDisabledUntilUsed() {
return getAppInfo().enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
}
private void forceStopPackage(String pkgName) {
ActivityManager am = (ActivityManager) getSystemService(
Context.ACTIVITY_SERVICE);
Log.d(TAG, "Stopping package " + pkgName);
am.forceStopPackage(pkgName);
updateForceStopButton();
}
// enable or disable the force stop button:
// - disabled if it's a device admin
// - if the application is stopped unexplicitly, enabled the button
// - if there's a reason for the system to restart the application, that indicates the app
// can be force stopped.
private void updateForceStopButton() {
if (mDpm.packageHasActiveAdmins(mResolveInfo.activityInfo.packageName)) {
// User can't force stop device admin.
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Disabling button, user can't force stop device admin");
}
mForceStopButton.setEnabled(false);
} else if ((getAppInfo().flags & ApplicationInfo.FLAG_STOPPED) == 0) {
// If the app isn't explicitly stopped, then always show the
// force stop button.
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "App is not explicitly stopped");
}
mForceStopButton.setEnabled(true);
} else {
Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
Uri.fromParts("package", mResolveInfo.activityInfo.packageName, null));
intent.putExtra(Intent.EXTRA_PACKAGES, new String[]{
mResolveInfo.activityInfo.packageName
});
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Sending broadcast to query restart status for "
+ mResolveInfo.activityInfo.packageName);
}
sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
}
}
private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG,
MessageFormat.format("Got broadcast response: Restart status for {0} {1}",
mResolveInfo.activityInfo.packageName, enabled));
}
mForceStopButton.setEnabled(enabled);
}
};
private final PermissionsResultCallback mPermissionCallback
= new PermissionsResultCallback() {
@Override
public void onPermissionSummaryResult(int standardGrantedPermissionCount,
int requestedPermissionCount, int additionalGrantedPermissionCount,
List<CharSequence> grantedGroupLabels) {
Resources res = getResources();
CharSequence summary = null;
if (requestedPermissionCount == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_requested);
mPermissionContainer.setEnabled(false);
mPermissionContainer.setOnClickListener(null);
} else {
ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels);
if (additionalGrantedPermissionCount > 0) {
// N additional permissions.
list.add(res.getQuantityString(
R.plurals.runtime_permissions_additional_count,
additionalGrantedPermissionCount, additionalGrantedPermissionCount));
}
if (list.size() == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_granted);
} else {
summary = ListFormatter.getInstance().format(list);
}
mPermissionContainer.setEnabled(true);
mPermissionContainer.setOnClickListener(mPermissionClickedListener);
}
mPermissionDetailView.setText(summary);
}
};
private OnClickListener mPermissionClickedListener = new OnClickListener() {
@Override
public void onClick(View v) {
// start new activity to manage app permissions
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mResolveInfo.activityInfo.packageName);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w(TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS");
}
}
};
}