• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.car.settings.applications;
17 
18 import android.app.Activity;
19 import android.app.ActivityManager;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.UserHandle;
31 import android.view.View;
32 import android.widget.Button;
33 
34 import androidx.car.widget.ListItem;
35 import androidx.car.widget.ListItemProvider;
36 import androidx.car.widget.TextListItem;
37 
38 import com.android.car.settings.R;
39 import com.android.car.settings.common.ListItemSettingsFragment;
40 import com.android.car.settings.common.Logger;
41 import com.android.settingslib.Utils;
42 
43 import java.text.MessageFormat;
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 /**
48  * Shows details about an application and action associated with that application,
49  * like uninstall, forceStop.
50  */
51 public class ApplicationDetailFragment extends ListItemSettingsFragment {
52     private static final Logger LOG = new Logger(ApplicationDetailFragment.class);
53     public static final String EXTRA_RESOLVE_INFO = "extra_resolve_info";
54 
55     private ResolveInfo mResolveInfo;
56     private PackageInfo mPackageInfo;
57 
58     private Button mDisableToggle;
59     private Button mForceStopButton;
60     private DevicePolicyManager mDpm;
61 
getInstance(ResolveInfo resolveInfo)62     public static ApplicationDetailFragment getInstance(ResolveInfo resolveInfo) {
63         ApplicationDetailFragment applicationDetailFragment = new ApplicationDetailFragment();
64         Bundle bundle = ListItemSettingsFragment.getBundle();
65         bundle.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
66         bundle.putInt(EXTRA_TITLE_ID, R.string.applications_settings);
67         bundle.putInt(EXTRA_ACTION_BAR_LAYOUT, R.layout.action_bar_with_button);
68         applicationDetailFragment.setArguments(bundle);
69         return applicationDetailFragment;
70     }
71 
72     @Override
onCreate(Bundle savedInstanceState)73     public void onCreate(Bundle savedInstanceState) {
74         super.onCreate(savedInstanceState);
75         mResolveInfo = getArguments().getParcelable(EXTRA_RESOLVE_INFO);
76     }
77 
78     @Override
onActivityCreated(Bundle savedInstanceState)79     public void onActivityCreated(Bundle savedInstanceState) {
80         mPackageInfo = getPackageInfo();
81         super.onActivityCreated(savedInstanceState);
82         if (mResolveInfo == null) {
83             LOG.w("No application info set.");
84             return;
85         }
86 
87         mDisableToggle = (Button) getActivity().findViewById(R.id.action_button1);
88         mForceStopButton = (Button) getActivity().findViewById(R.id.action_button2);
89         mForceStopButton.setText(R.string.force_stop);
90         mForceStopButton.setVisibility(View.VISIBLE);
91 
92         mDpm = (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
93         updateForceStopButton();
94         mForceStopButton.setOnClickListener(
95                 v -> forceStopPackage(mResolveInfo.activityInfo.packageName));
96     }
97 
98     @Override
onStart()99     public void onStart() {
100         super.onStart();
101         updateForceStopButton();
102         updateDisableable();
103     }
104 
105     @Override
getItemProvider()106     public ListItemProvider getItemProvider() {
107         return new ListItemProvider.ListProvider(getListItems());
108     }
109 
getListItems()110     private List<ListItem> getListItems() {
111         ArrayList<ListItem> items = new ArrayList<>();
112         items.add(new ApplicationLineItem(
113                 getContext(),
114                 getContext().getPackageManager(),
115                 mResolveInfo,
116                 /* fragmentController= */ null,
117                 false));
118         items.add(new ApplicationPermissionLineItem(getContext(), mResolveInfo, this));
119         TextListItem versionItem = new TextListItem(getContext());
120         versionItem.setTitle(getContext().getString(
121                 R.string.application_version_label, mPackageInfo.versionName));
122         items.add(versionItem);
123         return items;
124     }
125 
126     // fetch the latest ApplicationInfo instead of caching it so it reflects the current state.
getAppInfo()127     private ApplicationInfo getAppInfo() {
128         try {
129             return getContext().getPackageManager().getApplicationInfo(
130                     mResolveInfo.activityInfo.packageName, 0 /* flag */);
131         } catch (PackageManager.NameNotFoundException e) {
132             LOG.e("incorrect packagename: " + mResolveInfo.activityInfo.packageName, e);
133             throw new IllegalArgumentException(e);
134         }
135     }
136 
getPackageInfo()137     private PackageInfo getPackageInfo() {
138         try {
139             return getContext().getPackageManager().getPackageInfo(
140                     mResolveInfo.activityInfo.packageName, 0 /* flag */);
141         } catch (PackageManager.NameNotFoundException e) {
142             LOG.e("incorrect packagename: " + mResolveInfo.activityInfo.packageName, e);
143             throw new IllegalArgumentException(e);
144         }
145     }
146 
updateDisableable()147     private void updateDisableable() {
148         boolean disableable = false;
149         boolean disabled = false;
150         // Try to prevent the user from bricking their phone
151         // by not allowing disabling of apps in the system.
152         if (Utils.isSystemPackage(
153                 getResources(), getContext().getPackageManager(), mPackageInfo)) {
154             // Disable button for core system applications.
155             mDisableToggle.setText(R.string.disable_text);
156             disabled = false;
157         } else if (getAppInfo().enabled && !isDisabledUntilUsed()) {
158             mDisableToggle.setText(R.string.disable_text);
159             disableable = true;
160             disabled = false;
161         } else {
162             mDisableToggle.setText(R.string.enable_text);
163             disableable = true;
164             disabled = true;
165         }
166         mDisableToggle.setEnabled(disableable);
167         final int enableState = disabled
168                 ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
169                 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
170         mDisableToggle.setOnClickListener(v -> {
171             getContext().getPackageManager().setApplicationEnabledSetting(
172                     mResolveInfo.activityInfo.packageName,
173                     enableState,
174                     0);
175             updateDisableable();
176         });
177     }
178 
isDisabledUntilUsed()179     private boolean isDisabledUntilUsed() {
180         return getAppInfo().enabledSetting
181                 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
182     }
183 
forceStopPackage(String pkgName)184     private void forceStopPackage(String pkgName) {
185         ActivityManager am = (ActivityManager) getContext().getSystemService(
186                 Context.ACTIVITY_SERVICE);
187         LOG.d("Stopping package " + pkgName);
188         am.forceStopPackage(pkgName);
189         updateForceStopButton();
190     }
191 
192     // enable or disable the force stop button:
193     // - disabled if it's a device admin
194     // - if the application is stopped unexplicitly, enabled the button
195     // - if there's a reason for the system to restart the application, that indicates the app
196     //   can be force stopped.
updateForceStopButton()197     private void updateForceStopButton() {
198         if (mDpm.packageHasActiveAdmins(mResolveInfo.activityInfo.packageName)) {
199             // User can't force stop device admin.
200             LOG.d("Disabling button, user can't force stop device admin");
201             mForceStopButton.setEnabled(false);
202         } else if ((getAppInfo().flags & ApplicationInfo.FLAG_STOPPED) == 0) {
203             // If the app isn't explicitly stopped, then always show the
204             // force stop button.
205             LOG.w("App is not explicitly stopped");
206             mForceStopButton.setEnabled(true);
207         } else {
208             Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
209                     Uri.fromParts("package", mResolveInfo.activityInfo.packageName, null));
210             intent.putExtra(Intent.EXTRA_PACKAGES, new String[]{
211                     mResolveInfo.activityInfo.packageName
212             });
213             LOG.d("Sending broadcast to query restart for "
214                     + mResolveInfo.activityInfo.packageName);
215             getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
216                     mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
217         }
218     }
219 
220     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
221         @Override
222         public void onReceive(Context context, Intent intent) {
223             final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
224             LOG.d(MessageFormat.format("Got broadcast response: Restart status for {0} {1}",
225                     mResolveInfo.activityInfo.packageName, enabled));
226             mForceStopButton.setEnabled(enabled);
227         }
228     };
229 }
230