1 /*
2  * Copyright (C) 2022 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.settings.applications;
18 
19 import android.annotation.IntDef;
20 import android.app.AppOpsManager;
21 import android.content.Context;
22 import android.os.Build;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.settingslib.applications.ApplicationsState;
27 import com.android.settingslib.applications.ApplicationsState.AppEntry;
28 import com.android.settingslib.applications.ApplicationsState.AppFilter;
29 import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 
34 /**
35  * Class for bridging the app battery usage information to ApplicationState.
36  */
37 public class AppStateAppBatteryUsageBridge extends AppStateBaseBridge {
38     private static final String TAG = AppStateAppBatteryUsageBridge.class.getSimpleName();
39     static final boolean DEBUG = Build.IS_DEBUGGABLE;
40 
41     @VisibleForTesting
42     Context mContext;
43     @VisibleForTesting
44     AppOpsManager mAppOpsManager;
45     @VisibleForTesting
46     PowerAllowlistBackend mPowerAllowlistBackend;
47 
48     @VisibleForTesting
49     static final int MODE_UNKNOWN = 0;
50     @VisibleForTesting
51     static final int MODE_UNRESTRICTED = 1;
52     @VisibleForTesting
53     static final int MODE_OPTIMIZED = 2;
54     @VisibleForTesting
55     static final int MODE_RESTRICTED = 3;
56 
57     @IntDef(
58             prefix = {"MODE_"},
59             value = {
60                     MODE_UNKNOWN,
61                     MODE_RESTRICTED,
62                     MODE_UNRESTRICTED,
63                     MODE_OPTIMIZED,
64             })
65     @Retention(RetentionPolicy.SOURCE)
66     @interface OptimizationMode {
67     }
68 
AppStateAppBatteryUsageBridge( Context context, ApplicationsState appState, Callback callback)69     public AppStateAppBatteryUsageBridge(
70             Context context, ApplicationsState appState, Callback callback) {
71         super(appState, callback);
72         mContext = context;
73         mAppOpsManager = context.getSystemService(AppOpsManager.class);
74         mPowerAllowlistBackend = PowerAllowlistBackend.getInstance(mContext);
75     }
76 
77     @Override
updateExtraInfo(AppEntry app, String pkg, int uid)78     protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
79         app.extraInfo = getAppBatteryUsageState(pkg, uid);
80     }
81 
82     @Override
loadAllExtraInfo()83     protected void loadAllExtraInfo() {
84         if (DEBUG) {
85             Log.d(TAG, "Start loadAllExtraInfo()");
86         }
87         mAppSession.getAllApps().stream().forEach(appEntry ->
88                 updateExtraInfo(appEntry, appEntry.info.packageName, appEntry.info.uid));
89         if (DEBUG) {
90             Log.d(TAG, "End loadAllExtraInfo()");
91         }
92     }
93 
getAppBatteryUsageState(String pkg, int uid)94     protected Object getAppBatteryUsageState(String pkg, int uid) {
95         // Restricted = AppOpsManager.MODE_IGNORED + !allowListed
96         // Unrestricted = AppOpsManager.MODE_ALLOWED + allowListed
97         // Optimized = AppOpsManager.MODE_ALLOWED + !allowListed
98 
99         boolean allowListed = mPowerAllowlistBackend.isAllowlisted(pkg, uid);
100         int aomMode =
101                 mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, pkg);
102         @OptimizationMode int mode = MODE_UNKNOWN;
103         String modeName = "";
104         if (aomMode == AppOpsManager.MODE_IGNORED && !allowListed) {
105             mode = MODE_RESTRICTED;
106             if (DEBUG) {
107                 modeName = "RESTRICTED";
108             }
109         } else if (aomMode == AppOpsManager.MODE_ALLOWED) {
110             mode = allowListed ? MODE_UNRESTRICTED : MODE_OPTIMIZED;
111             if (DEBUG) {
112                 modeName = allowListed ? "UNRESTRICTED" : "OPTIMIZED";
113             }
114         }
115         if (DEBUG) {
116             Log.d(TAG, "Pkg: " + pkg + ", mode: " + modeName);
117         }
118         return new AppBatteryUsageDetails(mode);
119     }
120 
121     @VisibleForTesting
122     @OptimizationMode
getAppBatteryUsageDetailsMode(AppEntry entry)123     static int getAppBatteryUsageDetailsMode(AppEntry entry) {
124         if (entry == null || entry.extraInfo == null) {
125             return MODE_UNKNOWN;
126         }
127 
128         return entry.extraInfo instanceof AppBatteryUsageDetails
129                 ? ((AppBatteryUsageDetails) entry.extraInfo).mMode
130                 : MODE_UNKNOWN;
131     }
132 
133     /**
134      * Used by {@link com.android.settings.applications.manageapplications.AppFilterRegistry} to
135      * determine which apps are unrestricted.
136      */
137     public static final AppFilter FILTER_BATTERY_UNRESTRICTED_APPS =
138             new AppFilter() {
139                 @Override
140                 public void init() {}
141 
142                 @Override
143                 public boolean filterApp(AppEntry info) {
144                     return getAppBatteryUsageDetailsMode(info) == MODE_UNRESTRICTED;
145                 }
146             };
147 
148     /**
149      * Used by {@link com.android.settings.applications.manageapplications.AppFilterRegistry} to
150      * determine which apps are optimized.
151      */
152     public static final AppFilter FILTER_BATTERY_OPTIMIZED_APPS =
153             new AppFilter() {
154                 @Override
155                 public void init() {}
156 
157                 @Override
158                 public boolean filterApp(AppEntry info) {
159                     return getAppBatteryUsageDetailsMode(info) == MODE_OPTIMIZED;
160                 }
161             };
162 
163     /**
164      * Used by {@link com.android.settings.applications.manageapplications.AppFilterRegistry} to
165      * determine which apps are restricted.
166      */
167     public static final AppFilter FILTER_BATTERY_RESTRICTED_APPS =
168             new AppFilter() {
169                 @Override
170                 public void init() {}
171 
172                 @Override
173                 public boolean filterApp(AppEntry info) {
174                     return getAppBatteryUsageDetailsMode(info) == MODE_RESTRICTED;
175                 }
176             };
177 
178     /**
179      * Extra details for app battery usage data.
180      */
181     static final class AppBatteryUsageDetails {
182         @OptimizationMode
183         int mMode;
184 
AppBatteryUsageDetails(@ptimizationMode int mode)185         AppBatteryUsageDetails(@OptimizationMode int mode) {
186             mMode = mode;
187         }
188     }
189 }
190