OSDN Git Service

Merge "Fix NPE crash in AppInfoBase" am: e49cc3d659 am: abcb069d95
[android-x86/packages-apps-Settings.git] / src / com / android / settings / fuelgauge / BackgroundActivityPreferenceController.java
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14
15 package com.android.settings.fuelgauge;
16
17 import android.app.AlertDialog;
18 import android.app.AppOpsManager;
19 import android.app.Dialog;
20 import android.app.Fragment;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.os.UserManager;
29 import android.support.annotation.VisibleForTesting;
30 import android.support.v14.preference.SwitchPreference;
31 import android.support.v7.preference.Preference;
32 import android.util.Log;
33
34 import com.android.settings.R;
35 import com.android.settings.Utils;
36 import com.android.settings.core.PreferenceControllerMixin;
37 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
38 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
39 import com.android.settingslib.core.AbstractPreferenceController;
40 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
41
42 /**
43  * Controller to control whether an app can run in the background
44  */
45 public class BackgroundActivityPreferenceController extends AbstractPreferenceController
46         implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
47
48     private static final String TAG = "BgActivityPrefContr";
49     private static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
50
51     private final PackageManager mPackageManager;
52     private final AppOpsManager mAppOpsManager;
53     private final UserManager mUserManager;
54     private final int mUid;
55     @VisibleForTesting
56     DevicePolicyManagerWrapper mDpm;
57     private Fragment mFragment;
58     private String mTargetPackage;
59     private boolean mIsPreOApp;
60     private PowerWhitelistBackend mPowerWhitelistBackend;
61
62     public BackgroundActivityPreferenceController(Context context, Fragment fragment,
63             int uid, String packageName) {
64         this(context, fragment, uid, packageName, PowerWhitelistBackend.getInstance());
65     }
66
67     @VisibleForTesting
68     BackgroundActivityPreferenceController(Context context, Fragment fragment,
69             int uid, String packageName, PowerWhitelistBackend backend) {
70         super(context);
71         mPowerWhitelistBackend = backend;
72         mPackageManager = context.getPackageManager();
73         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
74         mDpm = new DevicePolicyManagerWrapper(
75                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE));
76         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
77         mUid = uid;
78         mFragment = fragment;
79         mTargetPackage = packageName;
80         mIsPreOApp = isLegacyApp(packageName);
81     }
82
83     @Override
84     public void updateState(Preference preference) {
85         final int mode = mAppOpsManager
86                 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
87         final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage);
88         // Set checked or not before we may set it disabled
89         if (mode != AppOpsManager.MODE_ERRORED) {
90             final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED;
91             ((SwitchPreference) preference).setChecked(checked);
92         }
93         if (whitelisted || mode == AppOpsManager.MODE_ERRORED
94                 || Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) {
95             preference.setEnabled(false);
96         } else {
97             preference.setEnabled(true);
98         }
99         updateSummary(preference);
100     }
101
102     @Override
103     public boolean isAvailable() {
104         return mTargetPackage != null;
105     }
106
107     /**
108      * Called from the warning dialog, if the user decides to go ahead and disable background
109      * activity for this package
110      */
111     public void setUnchecked(Preference preference) {
112         if (mIsPreOApp) {
113             mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
114                     AppOpsManager.MODE_IGNORED);
115         }
116         mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage,
117                 AppOpsManager.MODE_IGNORED);
118         ((SwitchPreference) preference).setChecked(false);
119         updateSummary(preference);
120     }
121
122     @Override
123     public String getPreferenceKey() {
124         return KEY_BACKGROUND_ACTIVITY;
125     }
126
127     @Override
128     public boolean onPreferenceChange(Preference preference, Object newValue) {
129         final boolean switchOn = (Boolean) newValue;
130         if (!switchOn) {
131             final WarningDialogFragment dialogFragment = new WarningDialogFragment();
132             dialogFragment.setTargetFragment(mFragment, 0);
133             dialogFragment.show(mFragment.getFragmentManager(), TAG);
134             return false;
135         }
136         if (mIsPreOApp) {
137             mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage,
138                     AppOpsManager.MODE_ALLOWED);
139         }
140         mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage,
141                 AppOpsManager.MODE_ALLOWED);
142         updateSummary(preference);
143         return true;
144     }
145
146     @VisibleForTesting
147     boolean isLegacyApp(final String packageName) {
148         try {
149             ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
150                     PackageManager.GET_META_DATA);
151
152             return info.targetSdkVersion < Build.VERSION_CODES.O;
153         } catch (PackageManager.NameNotFoundException e) {
154             Log.e(TAG, "Cannot find package: " + packageName, e);
155         }
156
157         return false;
158     }
159
160     @VisibleForTesting
161     void updateSummary(Preference preference) {
162         if (mPowerWhitelistBackend.isWhitelisted(mTargetPackage)) {
163             preference.setSummary(R.string.background_activity_summary_whitelisted);
164             return;
165         }
166         final int mode = mAppOpsManager
167                 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
168
169         if (mode == AppOpsManager.MODE_ERRORED) {
170             preference.setSummary(R.string.background_activity_summary_disabled);
171         } else {
172             final boolean checked = mode != AppOpsManager.MODE_IGNORED;
173             preference.setSummary(checked ? R.string.background_activity_summary_on
174                     : R.string.background_activity_summary_off);
175         }
176     }
177
178     interface WarningConfirmationListener {
179         void onLimitBackgroundActivity();
180     }
181
182     /**
183      * Warning dialog to show to the user as turning off background activity can lead to
184      * apps misbehaving as their background task scheduling guarantees will no longer be honored.
185      */
186     public static class WarningDialogFragment extends InstrumentedDialogFragment {
187         @Override
188         public int getMetricsCategory() {
189             // TODO (b/65494831): add metric id
190             return 0;
191         }
192
193         @Override
194         public Dialog onCreateDialog(Bundle savedInstanceState) {
195             final WarningConfirmationListener listener =
196                     (WarningConfirmationListener) getTargetFragment();
197             return new AlertDialog.Builder(getContext())
198                     .setTitle(R.string.background_activity_warning_dialog_title)
199                     .setMessage(R.string.background_activity_warning_dialog_text)
200                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
201                         @Override
202                         public void onClick(DialogInterface dialog, int which) {
203                             listener.onLimitBackgroundActivity();
204                         }
205                     })
206                     .setNegativeButton(R.string.dlg_cancel, null)
207                     .create();
208         }
209     }
210 }