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      * Check if target package is in allow list except idle app
114      */
isAllowlistedExceptIdle(String pkg)115     public boolean isAllowlistedExceptIdle(String pkg) {
116         try {
117             return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
118         } catch (RemoteException e) {
119             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
120             return true;
121         }
122     }
123 
124     /**
125      *
126      * @param pkgs a list of packageName
127      * @return true when one of package is in allow list
128      */
isAllowlisted(String[] pkgs)129     public boolean isAllowlisted(String[] pkgs) {
130         if (ArrayUtils.isEmpty(pkgs)) {
131             return false;
132         }
133         for (String pkg : pkgs) {
134             if (isAllowlisted(pkg)) {
135                 return true;
136             }
137         }
138 
139         return false;
140     }
141 
142     /**
143      * Add app into power save allow list.
144      * @param pkg packageName
145      */
addApp(String pkg)146     public void addApp(String pkg) {
147         try {
148             mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
149             mAllowlistedApps.add(pkg);
150         } catch (RemoteException e) {
151             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
152         }
153     }
154 
155     /**
156      * Remove package from power save allow list.
157      * @param pkg
158      */
removeApp(String pkg)159     public void removeApp(String pkg) {
160         try {
161             mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
162             mAllowlistedApps.remove(pkg);
163         } catch (RemoteException e) {
164             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
165         }
166     }
167 
168     /**
169      * Refresh all of lists
170      */
171     @VisibleForTesting
refreshList()172     public void refreshList() {
173         mSysAllowlistedApps.clear();
174         mAllowlistedApps.clear();
175         mDefaultActiveApps.clear();
176         if (mDeviceIdleService == null) {
177             return;
178         }
179         try {
180             final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
181             for (String app : allowlistedApps) {
182                 mAllowlistedApps.add(app);
183             }
184             final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
185             for (String app : sysAllowlistedApps) {
186                 mSysAllowlistedApps.add(app);
187             }
188             final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
189                     PackageManager.FEATURE_TELEPHONY);
190             final ComponentName defaultSms = SmsApplication.getDefaultSmsApplication(mAppContext,
191                     true /* updateIfNeeded */);
192             final String defaultDialer = DefaultDialerManager.getDefaultDialerApplication(
193                     mAppContext);
194 
195             if (hasTelephony) {
196                 if (defaultSms != null) {
197                     mDefaultActiveApps.add(defaultSms.getPackageName());
198                 }
199                 if (!TextUtils.isEmpty(defaultDialer)) {
200                     mDefaultActiveApps.add(defaultDialer);
201                 }
202             }
203         } catch (RemoteException e) {
204             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
205         }
206     }
207 
208     /**
209      * @param context
210      * @return a PowerAllowlistBackend object
211      */
getInstance(Context context)212     public static PowerAllowlistBackend getInstance(Context context) {
213         if (sInstance == null) {
214             sInstance = new PowerAllowlistBackend(context);
215         }
216         return sInstance;
217     }
218 
219 }
220