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 whitelist for the exceptions to battery saving features.
38  */
39 public class PowerWhitelistBackend {
40 
41     private static final String TAG = "PowerWhitelistBackend";
42 
43     private static final String DEVICE_IDLE_SERVICE = "deviceidle";
44 
45     private static PowerWhitelistBackend sInstance;
46 
47     private final Context mAppContext;
48     private final IDeviceIdleController mDeviceIdleService;
49     private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
50     private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();
51     private final ArraySet<String> mSysWhitelistedAppsExceptIdle = new ArraySet<>();
52     private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
53 
PowerWhitelistBackend(Context context)54     public PowerWhitelistBackend(Context context) {
55         this(context, IDeviceIdleController.Stub.asInterface(
56                 ServiceManager.getService(DEVICE_IDLE_SERVICE)));
57     }
58 
59     @VisibleForTesting
PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService)60     PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService) {
61         mAppContext = context.getApplicationContext();
62         mDeviceIdleService = deviceIdleService;
63         refreshList();
64     }
65 
getWhitelistSize()66     public int getWhitelistSize() {
67         return mWhitelistedApps.size();
68     }
69 
isSysWhitelisted(String pkg)70     public boolean isSysWhitelisted(String pkg) {
71         return mSysWhitelistedApps.contains(pkg);
72     }
73 
isWhitelisted(String pkg)74     public boolean isWhitelisted(String pkg) {
75         if (mWhitelistedApps.contains(pkg)) {
76             return true;
77         }
78 
79         if (isDefaultActiveApp(pkg)) {
80             return true;
81         }
82 
83         return false;
84     }
85 
86     /**
87      * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..)
88      */
isDefaultActiveApp(String pkg)89     public boolean isDefaultActiveApp(String pkg) {
90         // Additionally, check if pkg is default dialer/sms. They are considered essential apps and
91         // should be automatically whitelisted (otherwise user may be able to set restriction on
92         // them, leading to bad device behavior.)
93 
94         if (mDefaultActiveApps.contains(pkg)) {
95             return true;
96         }
97 
98         final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
99                 DevicePolicyManager.class);
100         if (devicePolicyManager.packageHasActiveAdmins(pkg)) {
101             return true;
102         }
103 
104         return false;
105     }
106 
isWhitelisted(String[] pkgs)107     public boolean isWhitelisted(String[] pkgs) {
108         if (ArrayUtils.isEmpty(pkgs)) {
109             return false;
110         }
111         for (String pkg : pkgs) {
112             if (isWhitelisted(pkg)) {
113                 return true;
114             }
115         }
116 
117         return false;
118     }
119 
isSysWhitelistedExceptIdle(String pkg)120     public boolean isSysWhitelistedExceptIdle(String pkg) {
121         return mSysWhitelistedAppsExceptIdle.contains(pkg);
122     }
123 
isSysWhitelistedExceptIdle(String[] pkgs)124     public boolean isSysWhitelistedExceptIdle(String[] pkgs) {
125         if (ArrayUtils.isEmpty(pkgs)) {
126             return false;
127         }
128         for (String pkg : pkgs) {
129             if (isSysWhitelistedExceptIdle(pkg)) {
130                 return true;
131             }
132         }
133 
134         return false;
135     }
136 
addApp(String pkg)137     public void addApp(String pkg) {
138         try {
139             mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
140             mWhitelistedApps.add(pkg);
141         } catch (RemoteException e) {
142             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
143         }
144     }
145 
removeApp(String pkg)146     public void removeApp(String pkg) {
147         try {
148             mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
149             mWhitelistedApps.remove(pkg);
150         } catch (RemoteException e) {
151             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
152         }
153     }
154 
155     @VisibleForTesting
refreshList()156     public void refreshList() {
157         mSysWhitelistedApps.clear();
158         mSysWhitelistedAppsExceptIdle.clear();
159         mWhitelistedApps.clear();
160         mDefaultActiveApps.clear();
161         if (mDeviceIdleService == null) {
162             return;
163         }
164         try {
165             final String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist();
166             for (String app : whitelistedApps) {
167                 mWhitelistedApps.add(app);
168             }
169             final String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist();
170             for (String app : sysWhitelistedApps) {
171                 mSysWhitelistedApps.add(app);
172             }
173             final String[] sysWhitelistedAppsExceptIdle =
174                     mDeviceIdleService.getSystemPowerWhitelistExceptIdle();
175             for (String app : sysWhitelistedAppsExceptIdle) {
176                 mSysWhitelistedAppsExceptIdle.add(app);
177             }
178             final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
179                     PackageManager.FEATURE_TELEPHONY);
180             final ComponentName defaultSms = SmsApplication.getDefaultSmsApplication(mAppContext,
181                     true /* updateIfNeeded */);
182             final String defaultDialer = DefaultDialerManager.getDefaultDialerApplication(
183                     mAppContext);
184 
185             if (hasTelephony) {
186                 if (defaultSms != null) {
187                     mDefaultActiveApps.add(defaultSms.getPackageName());
188                 }
189                 if (!TextUtils.isEmpty(defaultDialer)) {
190                     mDefaultActiveApps.add(defaultDialer);
191                 }
192             }
193         } catch (RemoteException e) {
194             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
195         }
196     }
197 
getInstance(Context context)198     public static PowerWhitelistBackend getInstance(Context context) {
199         if (sInstance == null) {
200             sInstance = new PowerWhitelistBackend(context);
201         }
202         return sInstance;
203     }
204 
205 }
206