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