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