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.AppOpsManager; 18 import android.app.admin.DevicePolicyManager; 19 import android.content.Context; 20 import android.content.pm.ApplicationInfo; 21 import android.content.pm.PackageManager; 22 import android.os.Build; 23 import android.os.UserManager; 24 import android.support.annotation.VisibleForTesting; 25 import android.support.v14.preference.SwitchPreference; 26 import android.support.v7.preference.Preference; 27 import android.util.Log; 28 29 import com.android.settings.R; 30 import com.android.settings.Utils; 31 import com.android.settings.core.PreferenceControllerMixin; 32 import com.android.settings.enterprise.DevicePolicyManagerWrapper; 33 import com.android.settings.enterprise.DevicePolicyManagerWrapperImpl; 34 import com.android.settingslib.core.AbstractPreferenceController; 35 36 /** 37 * Controller to control whether an app can run in the background 38 */ 39 public class BackgroundActivityPreferenceController extends AbstractPreferenceController 40 implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 41 42 private static final String TAG = "BgActivityPrefContr"; 43 private static final String KEY_BACKGROUND_ACTIVITY = "background_activity"; 44 45 private final PackageManager mPackageManager; 46 private final AppOpsManager mAppOpsManager; 47 private final UserManager mUserManager; 48 private final String[] mPackages; 49 private final int mUid; 50 @VisibleForTesting 51 DevicePolicyManagerWrapper mDpm; 52 53 private String mTargetPackage; 54 BackgroundActivityPreferenceController(Context context, int uid)55 public BackgroundActivityPreferenceController(Context context, int uid) { 56 super(context); 57 mPackageManager = context.getPackageManager(); 58 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 59 mDpm = new DevicePolicyManagerWrapperImpl( 60 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE)); 61 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 62 mUid = uid; 63 mPackages = mPackageManager.getPackagesForUid(mUid); 64 } 65 66 @Override updateState(Preference preference)67 public void updateState(Preference preference) { 68 final int mode = mAppOpsManager 69 .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage); 70 // Set checked or not before we may set it disabled 71 if (mode != AppOpsManager.MODE_ERRORED) { 72 final boolean checked = mode != AppOpsManager.MODE_IGNORED; 73 ((SwitchPreference) preference).setChecked(checked); 74 } 75 if (mode == AppOpsManager.MODE_ERRORED 76 || Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) { 77 preference.setEnabled(false); 78 } 79 80 updateSummary(preference); 81 } 82 83 @Override isAvailable()84 public boolean isAvailable() { 85 if (mPackages == null) { 86 return false; 87 } 88 for (final String packageName : mPackages) { 89 if (isLegacyApp(packageName)) { 90 mTargetPackage = packageName; 91 return true; 92 } 93 } 94 95 return false; 96 } 97 98 @Override getPreferenceKey()99 public String getPreferenceKey() { 100 return KEY_BACKGROUND_ACTIVITY; 101 } 102 103 @Override onPreferenceChange(Preference preference, Object newValue)104 public boolean onPreferenceChange(Preference preference, Object newValue) { 105 boolean switchOn = (Boolean) newValue; 106 mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage, 107 switchOn ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 108 109 updateSummary(preference); 110 return true; 111 } 112 113 @VisibleForTesting getTargetPackage()114 String getTargetPackage() { 115 return mTargetPackage; 116 } 117 118 @VisibleForTesting isLegacyApp(final String packageName)119 boolean isLegacyApp(final String packageName) { 120 try { 121 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 122 PackageManager.GET_META_DATA); 123 124 return info.targetSdkVersion < Build.VERSION_CODES.O; 125 } catch (PackageManager.NameNotFoundException e) { 126 Log.e(TAG, "Cannot find package: " + packageName, e); 127 } 128 129 return false; 130 } 131 132 @VisibleForTesting updateSummary(Preference preference)133 void updateSummary(Preference preference) { 134 final int mode = mAppOpsManager 135 .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, mUid, mTargetPackage); 136 137 if (mode == AppOpsManager.MODE_ERRORED) { 138 preference.setSummary(R.string.background_activity_summary_disabled); 139 } else { 140 final boolean checked = mode != AppOpsManager.MODE_IGNORED; 141 preference.setSummary(checked ? R.string.background_activity_summary_on 142 : R.string.background_activity_summary_off); 143 } 144 } 145 } 146