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.settings.fuelgauge; 18 19 import android.annotation.IntDef; 20 import android.app.AppOpsManager; 21 import android.content.Context; 22 import android.os.AsyncTask; 23 import android.util.Log; 24 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.settingslib.fuelgauge.PowerAllowlistBackend; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 32 /** A utility class for application usage operation. */ 33 public class BatteryOptimizeUtils { 34 private static final String TAG = "BatteryOptimizeUtils"; 35 private static final String UNKNOWN_PACKAGE = "unknown"; 36 37 @VisibleForTesting AppOpsManager mAppOpsManager; 38 @VisibleForTesting BatteryUtils mBatteryUtils; 39 @VisibleForTesting PowerAllowlistBackend mPowerAllowListBackend; 40 @VisibleForTesting int mMode; 41 @VisibleForTesting boolean mAllowListed; 42 43 private final String mPackageName; 44 private final int mUid; 45 46 // Optimization modes. 47 static final int MODE_UNKNOWN = 0; 48 static final int MODE_RESTRICTED = 1; 49 static final int MODE_UNRESTRICTED = 2; 50 static final int MODE_OPTIMIZED = 3; 51 52 @IntDef(prefix = {"MODE_"}, value = { 53 MODE_UNKNOWN, 54 MODE_RESTRICTED, 55 MODE_UNRESTRICTED, 56 MODE_OPTIMIZED, 57 }) 58 @Retention(RetentionPolicy.SOURCE) 59 static @interface OptimizationMode {} 60 BatteryOptimizeUtils(Context context, int uid, String packageName)61 public BatteryOptimizeUtils(Context context, int uid, String packageName) { 62 mUid = uid; 63 mPackageName = packageName; 64 mAppOpsManager = context.getSystemService(AppOpsManager.class); 65 mBatteryUtils = BatteryUtils.getInstance(context); 66 mPowerAllowListBackend = PowerAllowlistBackend.getInstance(context); 67 mMode = mAppOpsManager 68 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName); 69 mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName); 70 } 71 72 /** Gets the {@link OptimizationMode} based on mode and allowed list. */ 73 @OptimizationMode getAppOptimizationMode(int mode, boolean isAllowListed)74 public static int getAppOptimizationMode(int mode, boolean isAllowListed) { 75 if (!isAllowListed && mode == AppOpsManager.MODE_IGNORED) { 76 return MODE_RESTRICTED; 77 } else if (isAllowListed && mode == AppOpsManager.MODE_ALLOWED) { 78 return MODE_UNRESTRICTED; 79 } else if (!isAllowListed && mode == AppOpsManager.MODE_ALLOWED) { 80 return MODE_OPTIMIZED; 81 } else { 82 return MODE_UNKNOWN; 83 } 84 } 85 86 /** Gets the {@link OptimizationMode} for associated app. */ 87 @OptimizationMode getAppOptimizationMode()88 public int getAppOptimizationMode() { 89 refreshState(); 90 return getAppOptimizationMode(mMode, mAllowListed); 91 } 92 93 /** Sets the {@link OptimizationMode} for associated app. */ setAppUsageState(@ptimizationMode int mode)94 public void setAppUsageState(@OptimizationMode int mode) { 95 if (getAppOptimizationMode(mMode, mAllowListed) == mode) { 96 Log.w(TAG, "set the same optimization mode for: " + mPackageName); 97 return; 98 } 99 100 AsyncTask.execute(() -> { 101 switch (mode) { 102 case MODE_RESTRICTED: 103 setAppOptimizationMode(AppOpsManager.MODE_IGNORED, /* allowListed */ false); 104 break; 105 case MODE_UNRESTRICTED: 106 setAppOptimizationMode(AppOpsManager.MODE_ALLOWED, /* allowListed */ true); 107 break; 108 case MODE_OPTIMIZED: 109 setAppOptimizationMode(AppOpsManager.MODE_ALLOWED, /* allowListed */ false); 110 break; 111 default: 112 Log.d(TAG, "set unknown app optimization mode."); 113 } 114 }); 115 } 116 117 /** 118 * Return {@code true} if package name is valid (can get an uid). 119 */ isValidPackageName()120 public boolean isValidPackageName() { 121 return mBatteryUtils.getPackageUid(mPackageName) != BatteryUtils.UID_NULL; 122 } 123 124 /** 125 * Return {@code true} if this package is system or default active app. 126 */ isSystemOrDefaultApp()127 public boolean isSystemOrDefaultApp() { 128 mPowerAllowListBackend.refreshList(); 129 130 return mPowerAllowListBackend.isSysAllowlisted(mPackageName) 131 || mPowerAllowListBackend.isDefaultActiveApp(mPackageName); 132 } 133 getPackageName()134 String getPackageName() { 135 return mPackageName == null ? UNKNOWN_PACKAGE : mPackageName; 136 } 137 setAppOptimizationMode(int appStandbyMode, boolean allowListed)138 private void setAppOptimizationMode(int appStandbyMode, boolean allowListed) { 139 try { 140 mBatteryUtils.setForceAppStandby(mUid, mPackageName, appStandbyMode); 141 if (allowListed) { 142 mPowerAllowListBackend.addApp(mPackageName); 143 } else { 144 mPowerAllowListBackend.removeApp(mPackageName); 145 } 146 } catch (Exception e) { 147 Log.e(TAG, "set OPTIMIZED failed for " + mPackageName, e); 148 } 149 } 150 refreshState()151 private void refreshState() { 152 mPowerAllowListBackend.refreshList(); 153 mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName); 154 mMode = mAppOpsManager 155 .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName); 156 Log.d(TAG, String.format("refresh %s state, allowlisted = %s, mode = %d", 157 mPackageName, 158 mAllowListed, 159 mMode)); 160 } 161 } 162