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 17 package com.android.settingslib.fuelgauge; 18 19 import static android.provider.DeviceConfig.NAMESPACE_ACTIVITY_MANAGER; 20 21 import android.app.AppOpsManager; 22 import android.app.admin.DevicePolicyManager; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.pm.PackageManager; 26 import android.os.IDeviceIdleController; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.provider.DeviceConfig; 30 import android.telecom.DefaultDialerManager; 31 import android.text.TextUtils; 32 import android.util.ArraySet; 33 import android.util.Log; 34 35 import androidx.annotation.VisibleForTesting; 36 37 import com.android.internal.telephony.SmsApplication; 38 import com.android.internal.util.ArrayUtils; 39 40 /** 41 * Handles getting/changing the allowlist for the exceptions to battery saving features. 42 */ 43 public class PowerAllowlistBackend { 44 45 private static final String TAG = "PowerAllowlistBackend"; 46 47 private static final String DEVICE_IDLE_SERVICE = "deviceidle"; 48 49 private static final String SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED = 50 "system_exempt_power_restrictions_enabled"; 51 private static final boolean DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED = true; 52 53 private static PowerAllowlistBackend sInstance; 54 55 private final Context mAppContext; 56 private final IDeviceIdleController mDeviceIdleService; 57 private final ArraySet<String> mAllowlistedApps = new ArraySet<>(); 58 private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>(); 59 private final ArraySet<String> mDefaultActiveApps = new ArraySet<>(); 60 PowerAllowlistBackend(Context context)61 public PowerAllowlistBackend(Context context) { 62 this(context, IDeviceIdleController.Stub.asInterface( 63 ServiceManager.getService(DEVICE_IDLE_SERVICE))); 64 } 65 66 @VisibleForTesting PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService)67 PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService) { 68 mAppContext = context.getApplicationContext(); 69 mDeviceIdleService = deviceIdleService; 70 refreshList(); 71 } 72 getAllowlistSize()73 public int getAllowlistSize() { 74 return mAllowlistedApps.size(); 75 } 76 77 /** 78 * Check if target package is in System allow list 79 */ isSysAllowlisted(String pkg)80 public boolean isSysAllowlisted(String pkg) { 81 return mSysAllowlistedApps.contains(pkg); 82 } 83 84 /** 85 * Check if target package is in allow list 86 */ isAllowlisted(String pkg, int uid)87 public boolean isAllowlisted(String pkg, int uid) { 88 if (mAllowlistedApps.contains(pkg)) { 89 return true; 90 } 91 92 if (isDefaultActiveApp(pkg, uid)) { 93 return true; 94 } 95 96 return false; 97 } 98 99 /** 100 * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..) 101 */ isDefaultActiveApp(String pkg, int uid)102 public boolean isDefaultActiveApp(String pkg, int uid) { 103 // Additionally, check if pkg is default dialer/sms. They are considered essential apps and 104 // should be automatically allowlisted (otherwise user may be able to set restriction on 105 // them, leading to bad device behavior.) 106 107 if (mDefaultActiveApps.contains(pkg)) { 108 return true; 109 } 110 111 final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService( 112 DevicePolicyManager.class); 113 if (devicePolicyManager.packageHasActiveAdmins(pkg)) { 114 return true; 115 } 116 117 final AppOpsManager appOpsManager = mAppContext.getSystemService(AppOpsManager.class); 118 if (isSystemExemptFlagEnabled() && appOpsManager.checkOpNoThrow( 119 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, uid, pkg) 120 == AppOpsManager.MODE_ALLOWED) { 121 return true; 122 } 123 124 return false; 125 } 126 isSystemExemptFlagEnabled()127 private static boolean isSystemExemptFlagEnabled() { 128 return DeviceConfig.getBoolean( 129 NAMESPACE_ACTIVITY_MANAGER, 130 SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED, 131 DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED); 132 } 133 134 /** 135 * Check if target package is in allow list except idle app 136 */ isAllowlistedExceptIdle(String pkg)137 public boolean isAllowlistedExceptIdle(String pkg) { 138 try { 139 return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg); 140 } catch (RemoteException e) { 141 Log.w(TAG, "Unable to reach IDeviceIdleController", e); 142 return true; 143 } 144 } 145 146 /** 147 * 148 * @param pkgs a list of packageName 149 * @return true when one of package is in allow list 150 */ isAllowlisted(String[] pkgs, int uid)151 public boolean isAllowlisted(String[] pkgs, int uid) { 152 if (ArrayUtils.isEmpty(pkgs)) { 153 return false; 154 } 155 for (String pkg : pkgs) { 156 if (isAllowlisted(pkg, uid)) { 157 return true; 158 } 159 } 160 161 return false; 162 } 163 164 /** 165 * Add app into power save allow list. 166 * @param pkg packageName 167 */ addApp(String pkg)168 public void addApp(String pkg) { 169 try { 170 mDeviceIdleService.addPowerSaveWhitelistApp(pkg); 171 mAllowlistedApps.add(pkg); 172 } catch (RemoteException e) { 173 Log.w(TAG, "Unable to reach IDeviceIdleController", e); 174 } 175 } 176 177 /** 178 * Remove package from power save allow list. 179 * @param pkg 180 */ removeApp(String pkg)181 public void removeApp(String pkg) { 182 try { 183 mDeviceIdleService.removePowerSaveWhitelistApp(pkg); 184 mAllowlistedApps.remove(pkg); 185 } catch (RemoteException e) { 186 Log.w(TAG, "Unable to reach IDeviceIdleController", e); 187 } 188 } 189 190 /** 191 * Refresh all of lists 192 */ 193 @VisibleForTesting refreshList()194 public void refreshList() { 195 mSysAllowlistedApps.clear(); 196 mAllowlistedApps.clear(); 197 mDefaultActiveApps.clear(); 198 if (mDeviceIdleService == null) { 199 return; 200 } 201 try { 202 final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist(); 203 for (String app : allowlistedApps) { 204 mAllowlistedApps.add(app); 205 } 206 final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist(); 207 for (String app : sysAllowlistedApps) { 208 mSysAllowlistedApps.add(app); 209 } 210 final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature( 211 PackageManager.FEATURE_TELEPHONY); 212 final ComponentName defaultSms = SmsApplication.getDefaultSmsApplication(mAppContext, 213 true /* updateIfNeeded */); 214 final String defaultDialer = DefaultDialerManager.getDefaultDialerApplication( 215 mAppContext); 216 217 if (hasTelephony) { 218 if (defaultSms != null) { 219 mDefaultActiveApps.add(defaultSms.getPackageName()); 220 } 221 if (!TextUtils.isEmpty(defaultDialer)) { 222 mDefaultActiveApps.add(defaultDialer); 223 } 224 } 225 } catch (RemoteException e) { 226 Log.w(TAG, "Unable to reach IDeviceIdleController", e); 227 } 228 } 229 230 /** 231 * @param context 232 * @return a PowerAllowlistBackend object 233 */ getInstance(Context context)234 public static PowerAllowlistBackend getInstance(Context context) { 235 if (sInstance == null) { 236 sInstance = new PowerAllowlistBackend(context); 237 } 238 return sInstance; 239 } 240 241 } 242