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