Loading src/com/android/settings/fuelgauge/BatteryBackupHelper.java +116 −18 Original line number Diff line number Diff line Loading @@ -16,21 +16,33 @@ package com.android.settings.fuelgauge; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; import android.app.backup.BackupHelper; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.os.IDeviceIdleController; import android.os.RemoteException; import android.os.ParcelFileDescriptor; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** An implementation to backup and restore battery configurations. */ public final class BatteryBackupHelper implements BackupHelper { Loading @@ -39,13 +51,24 @@ public final class BatteryBackupHelper implements BackupHelper { private static final String DEVICE_IDLE_SERVICE = "deviceidle"; private static final boolean DEBUG = false; @VisibleForTesting static final CharSequence DELIMITER = ":"; @VisibleForTesting // Only the owner can see all apps. private static final int RETRIEVE_FLAG_ADMIN = PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; private static final int RETRIEVE_FLAG = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; static final CharSequence DELIMITER = ","; static final CharSequence DELIMITER_MODE = "|"; static final String KEY_FULL_POWER_LIST = "full_power_list"; static final String KEY_OPTIMIZATION_LIST = "optimization_mode_list"; @VisibleForTesting IDeviceIdleController mIDeviceIdleController; @VisibleForTesting IPackageManager mIPackageManager; private final Context mContext; Loading @@ -57,10 +80,13 @@ public final class BatteryBackupHelper implements BackupHelper { public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { if (!isOwner()) { Log.w(TAG, "ignore the backup process for non-owner"); Log.w(TAG, "ignore performBackup() for non-owner"); return; } backupFullPowerList(getIDeviceIdleController(), data); final List<String> allowlistedApps = backupFullPowerList(data); if (allowlistedApps != null) { backupOptimizationMode(data, allowlistedApps); } } @Override Loading @@ -72,33 +98,57 @@ public final class BatteryBackupHelper implements BackupHelper { public void writeNewStateDescription(ParcelFileDescriptor newState) { } private void backupFullPowerList( IDeviceIdleController deviceIdleService, BackupDataOutput data) { private List<String> backupFullPowerList(BackupDataOutput data) { final long timestamp = System.currentTimeMillis(); String[] allowlistedApps; try { allowlistedApps = deviceIdleService.getFullPowerWhitelist(); allowlistedApps = getIDeviceIdleController().getFullPowerWhitelist(); } catch (RemoteException e) { Log.e(TAG, "backupFullPowerList() failed", e); return; return null; } // Ignores unexpected emptty result case. if (allowlistedApps == null || allowlistedApps.length == 0) { Log.w(TAG, "no data found in the getFullPowerList()"); return; return new ArrayList<>(); } debugLog("allowlistedApps:" + Arrays.toString(allowlistedApps)); final String allowedApps = String.join(DELIMITER, allowlistedApps); final byte[] allowedAppsBytes = allowedApps.getBytes(); try { data.writeEntityHeader(KEY_FULL_POWER_LIST, allowedAppsBytes.length); data.writeEntityData(allowedAppsBytes, allowedAppsBytes.length); } catch (IOException e) { Log.e(TAG, "backup getFullPowerList() failed", e); return; } writeBackupData(data, KEY_FULL_POWER_LIST, allowedApps); Log.d(TAG, String.format("backup getFullPowerList() size=%d in %d/ms", allowlistedApps.length, (System.currentTimeMillis() - timestamp))); return Arrays.asList(allowlistedApps); } @VisibleForTesting void backupOptimizationMode(BackupDataOutput data, List<String> allowlistedApps) { final long timestamp = System.currentTimeMillis(); final List<ApplicationInfo> applications = getInstalledApplications(); if (applications == null || applications.isEmpty()) { Log.w(TAG, "no data found in the getInstalledApplications()"); return; } final StringBuilder builder = new StringBuilder(); final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); // Converts application into the AppUsageState. for (ApplicationInfo info : applications) { final int mode = appOps.checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, info.uid, info.packageName); final AppUsageState state = BatteryOptimizeUtils.getAppUsageState( mode, allowlistedApps.contains(info.packageName)); // Ignores default optimized or unknown state. if (state == AppUsageState.OPTIMIZED || state == AppUsageState.UNKNOWN) { continue; } final String packageOptimizeMode = info.packageName + DELIMITER_MODE + state; builder.append(packageOptimizeMode + DELIMITER); debugLog(packageOptimizeMode); } writeBackupData(data, KEY_OPTIMIZATION_LIST, builder.toString()); Log.d(TAG, String.format("backup getInstalledApplications() size=%d in %d/ms", applications.size(), (System.currentTimeMillis() - timestamp))); } // Provides an opportunity to inject mock IDeviceIdleController for testing. Loading @@ -111,10 +161,58 @@ public final class BatteryBackupHelper implements BackupHelper { return mIDeviceIdleController; } private IPackageManager getIPackageManager() { if (mIPackageManager != null) { return mIPackageManager; } mIPackageManager = AppGlobals.getPackageManager(); return mIPackageManager; } private List<ApplicationInfo> getInstalledApplications() { final List<ApplicationInfo> applications = new ArrayList<>(); final UserManager um = mContext.getSystemService(UserManager.class); for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) { try { @SuppressWarnings("unchecked") final ParceledListSlice<ApplicationInfo> infoList = getIPackageManager().getInstalledApplications( userInfo.isAdmin() ? RETRIEVE_FLAG_ADMIN : RETRIEVE_FLAG, userInfo.id); if (infoList != null) { applications.addAll(infoList.getList()); } } catch (Exception e) { Log.e(TAG, "getInstalledApplications() is failed", e); return null; } } // Removes the application which is disabled by the system. for (int index = applications.size() - 1; index >= 0; index--) { final ApplicationInfo info = applications.get(index); if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER && !info.enabled) { applications.remove(index); } } return applications; } private void debugLog(String debugContent) { if (DEBUG) Log.d(TAG, debugContent); } private static void writeBackupData( BackupDataOutput data, String dataKey, String dataContent) { final byte[] dataContentBytes = dataContent.getBytes(); try { data.writeEntityHeader(dataKey, dataContentBytes.length); data.writeEntityData(dataContentBytes, dataContentBytes.length); } catch (IOException e) { Log.e(TAG, "writeBackupData() is failed for " + dataKey, e); } } private static boolean isOwner() { return UserHandle.myUserId() == UserHandle.USER_OWNER; } Loading src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +11 −6 Original line number Diff line number Diff line Loading @@ -59,20 +59,25 @@ public class BatteryOptimizeUtils { mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName); } public AppUsageState getAppUsageState() { refreshState(); if (!mAllowListed && mMode == AppOpsManager.MODE_IGNORED) { /** Gets the {@link AppUsageState} based on mode and allowed list. */ public static AppUsageState getAppUsageState(int mode, boolean isAllowListed) { if (!isAllowListed && mode == AppOpsManager.MODE_IGNORED) { return AppUsageState.RESTRICTED; } else if (mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) { } else if (isAllowListed && mode == AppOpsManager.MODE_ALLOWED) { return AppUsageState.UNRESTRICTED; } else if (!mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) { } else if (!isAllowListed && mode == AppOpsManager.MODE_ALLOWED) { return AppUsageState.OPTIMIZED; } else { Log.d(TAG, "get unknown app usage state."); return AppUsageState.UNKNOWN; } } /** Gets the current {@link AppUsageState}. */ public AppUsageState getAppUsageState() { refreshState(); return getAppUsageState(mMode, mAllowListed); } public void setAppUsageState(AppUsageState state) { switch (state) { case RESTRICTED: Loading tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +92 −0 Original line number Diff line number Diff line Loading @@ -27,11 +27,21 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.backup.BackupDataOutput; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.os.IDeviceIdleController; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import java.util.Arrays; import java.util.List; import org.junit.After; import org.junit.Before; Loading @@ -57,13 +67,23 @@ public final class BatteryBackupHelperTest { private BackupDataOutput mBackupDataOutput; @Mock private IDeviceIdleController mDeviceController; @Mock private IPackageManager mIPackageManager; @Mock private AppOpsManager mAppOpsManager; @Mock private UserManager mUserManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); mBatteryBackupHelper = new BatteryBackupHelper(mContext); mBatteryBackupHelper.mIDeviceIdleController = mDeviceController; mBatteryBackupHelper.mIPackageManager = mIPackageManager; } @After Loading Loading @@ -135,6 +155,78 @@ public final class BatteryBackupHelperTest { verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); } @Test public void backupOptimizationMode_nullInstalledApps_ignoreBackupOptimization() throws Exception { final UserInfo userInfo = new UserInfo(/*userId=*/ 0, /*userName=*/ "google", /*flag=*/ 0); doReturn(Arrays.asList(userInfo)).when(mUserManager).getProfiles(anyInt()); doThrow(new RuntimeException()) .when(mIPackageManager) .getInstalledApplications(anyInt(), anyInt()); mBatteryBackupHelper.backupOptimizationMode(mBackupDataOutput, null); verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); } @Test public void backupOptimizationMode_backupOptimizationMode() throws Exception { final String packageName1 = "com.android.testing.1"; final String packageName2 = "com.android.testing.2"; final String packageName3 = "com.android.testing.3"; final List<String> allowlistedApps = Arrays.asList(packageName1); createTestingData(packageName1, packageName2, packageName3); mBatteryBackupHelper.backupOptimizationMode(mBackupDataOutput, allowlistedApps); final String expectedResult = packageName1 + "|UNRESTRICTED," + packageName2 + "|RESTRICTED,"; final byte[] expectedBytes = expectedResult.getBytes(); verify(mBackupDataOutput).writeEntityHeader( BatteryBackupHelper.KEY_OPTIMIZATION_LIST, expectedBytes.length); verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); } private void createTestingData( String packageName1, String packageName2, String packageName3) throws Exception { // Sets the getInstalledApplications() method for testing. final UserInfo userInfo = new UserInfo(/*userId=*/ 0, /*userName=*/ "google", /*flag=*/ 0); doReturn(Arrays.asList(userInfo)).when(mUserManager).getProfiles(anyInt()); final ApplicationInfo applicationInfo1 = new ApplicationInfo(); applicationInfo1.enabled = true; applicationInfo1.uid = 1; applicationInfo1.packageName = packageName1; final ApplicationInfo applicationInfo2 = new ApplicationInfo(); applicationInfo2.enabled = false; applicationInfo2.uid = 2; applicationInfo2.packageName = packageName2; applicationInfo2.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; final ApplicationInfo applicationInfo3 = new ApplicationInfo(); applicationInfo3.enabled = false; applicationInfo3.uid = 3; applicationInfo3.packageName = packageName3; applicationInfo3.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED; doReturn(new ParceledListSlice<ApplicationInfo>( Arrays.asList(applicationInfo1, applicationInfo2, applicationInfo3))) .when(mIPackageManager) .getInstalledApplications(anyInt(), anyInt()); // Sets the AppOpsManager for checkOpNoThrow() method. doReturn(AppOpsManager.MODE_ALLOWED) .when(mAppOpsManager) .checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, applicationInfo1.uid, applicationInfo1.packageName); doReturn(AppOpsManager.MODE_IGNORED) .when(mAppOpsManager) .checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, applicationInfo2.uid, applicationInfo2.packageName); } @Implements(UserHandle.class) public static class ShadowUserHandle { // Sets the default as thte OWNER role. Loading Loading
src/com/android/settings/fuelgauge/BatteryBackupHelper.java +116 −18 Original line number Diff line number Diff line Loading @@ -16,21 +16,33 @@ package com.android.settings.fuelgauge; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; import android.app.backup.BackupHelper; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.os.IDeviceIdleController; import android.os.RemoteException; import android.os.ParcelFileDescriptor; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** An implementation to backup and restore battery configurations. */ public final class BatteryBackupHelper implements BackupHelper { Loading @@ -39,13 +51,24 @@ public final class BatteryBackupHelper implements BackupHelper { private static final String DEVICE_IDLE_SERVICE = "deviceidle"; private static final boolean DEBUG = false; @VisibleForTesting static final CharSequence DELIMITER = ":"; @VisibleForTesting // Only the owner can see all apps. private static final int RETRIEVE_FLAG_ADMIN = PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; private static final int RETRIEVE_FLAG = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; static final CharSequence DELIMITER = ","; static final CharSequence DELIMITER_MODE = "|"; static final String KEY_FULL_POWER_LIST = "full_power_list"; static final String KEY_OPTIMIZATION_LIST = "optimization_mode_list"; @VisibleForTesting IDeviceIdleController mIDeviceIdleController; @VisibleForTesting IPackageManager mIPackageManager; private final Context mContext; Loading @@ -57,10 +80,13 @@ public final class BatteryBackupHelper implements BackupHelper { public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { if (!isOwner()) { Log.w(TAG, "ignore the backup process for non-owner"); Log.w(TAG, "ignore performBackup() for non-owner"); return; } backupFullPowerList(getIDeviceIdleController(), data); final List<String> allowlistedApps = backupFullPowerList(data); if (allowlistedApps != null) { backupOptimizationMode(data, allowlistedApps); } } @Override Loading @@ -72,33 +98,57 @@ public final class BatteryBackupHelper implements BackupHelper { public void writeNewStateDescription(ParcelFileDescriptor newState) { } private void backupFullPowerList( IDeviceIdleController deviceIdleService, BackupDataOutput data) { private List<String> backupFullPowerList(BackupDataOutput data) { final long timestamp = System.currentTimeMillis(); String[] allowlistedApps; try { allowlistedApps = deviceIdleService.getFullPowerWhitelist(); allowlistedApps = getIDeviceIdleController().getFullPowerWhitelist(); } catch (RemoteException e) { Log.e(TAG, "backupFullPowerList() failed", e); return; return null; } // Ignores unexpected emptty result case. if (allowlistedApps == null || allowlistedApps.length == 0) { Log.w(TAG, "no data found in the getFullPowerList()"); return; return new ArrayList<>(); } debugLog("allowlistedApps:" + Arrays.toString(allowlistedApps)); final String allowedApps = String.join(DELIMITER, allowlistedApps); final byte[] allowedAppsBytes = allowedApps.getBytes(); try { data.writeEntityHeader(KEY_FULL_POWER_LIST, allowedAppsBytes.length); data.writeEntityData(allowedAppsBytes, allowedAppsBytes.length); } catch (IOException e) { Log.e(TAG, "backup getFullPowerList() failed", e); return; } writeBackupData(data, KEY_FULL_POWER_LIST, allowedApps); Log.d(TAG, String.format("backup getFullPowerList() size=%d in %d/ms", allowlistedApps.length, (System.currentTimeMillis() - timestamp))); return Arrays.asList(allowlistedApps); } @VisibleForTesting void backupOptimizationMode(BackupDataOutput data, List<String> allowlistedApps) { final long timestamp = System.currentTimeMillis(); final List<ApplicationInfo> applications = getInstalledApplications(); if (applications == null || applications.isEmpty()) { Log.w(TAG, "no data found in the getInstalledApplications()"); return; } final StringBuilder builder = new StringBuilder(); final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); // Converts application into the AppUsageState. for (ApplicationInfo info : applications) { final int mode = appOps.checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, info.uid, info.packageName); final AppUsageState state = BatteryOptimizeUtils.getAppUsageState( mode, allowlistedApps.contains(info.packageName)); // Ignores default optimized or unknown state. if (state == AppUsageState.OPTIMIZED || state == AppUsageState.UNKNOWN) { continue; } final String packageOptimizeMode = info.packageName + DELIMITER_MODE + state; builder.append(packageOptimizeMode + DELIMITER); debugLog(packageOptimizeMode); } writeBackupData(data, KEY_OPTIMIZATION_LIST, builder.toString()); Log.d(TAG, String.format("backup getInstalledApplications() size=%d in %d/ms", applications.size(), (System.currentTimeMillis() - timestamp))); } // Provides an opportunity to inject mock IDeviceIdleController for testing. Loading @@ -111,10 +161,58 @@ public final class BatteryBackupHelper implements BackupHelper { return mIDeviceIdleController; } private IPackageManager getIPackageManager() { if (mIPackageManager != null) { return mIPackageManager; } mIPackageManager = AppGlobals.getPackageManager(); return mIPackageManager; } private List<ApplicationInfo> getInstalledApplications() { final List<ApplicationInfo> applications = new ArrayList<>(); final UserManager um = mContext.getSystemService(UserManager.class); for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) { try { @SuppressWarnings("unchecked") final ParceledListSlice<ApplicationInfo> infoList = getIPackageManager().getInstalledApplications( userInfo.isAdmin() ? RETRIEVE_FLAG_ADMIN : RETRIEVE_FLAG, userInfo.id); if (infoList != null) { applications.addAll(infoList.getList()); } } catch (Exception e) { Log.e(TAG, "getInstalledApplications() is failed", e); return null; } } // Removes the application which is disabled by the system. for (int index = applications.size() - 1; index >= 0; index--) { final ApplicationInfo info = applications.get(index); if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER && !info.enabled) { applications.remove(index); } } return applications; } private void debugLog(String debugContent) { if (DEBUG) Log.d(TAG, debugContent); } private static void writeBackupData( BackupDataOutput data, String dataKey, String dataContent) { final byte[] dataContentBytes = dataContent.getBytes(); try { data.writeEntityHeader(dataKey, dataContentBytes.length); data.writeEntityData(dataContentBytes, dataContentBytes.length); } catch (IOException e) { Log.e(TAG, "writeBackupData() is failed for " + dataKey, e); } } private static boolean isOwner() { return UserHandle.myUserId() == UserHandle.USER_OWNER; } Loading
src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +11 −6 Original line number Diff line number Diff line Loading @@ -59,20 +59,25 @@ public class BatteryOptimizeUtils { mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName); } public AppUsageState getAppUsageState() { refreshState(); if (!mAllowListed && mMode == AppOpsManager.MODE_IGNORED) { /** Gets the {@link AppUsageState} based on mode and allowed list. */ public static AppUsageState getAppUsageState(int mode, boolean isAllowListed) { if (!isAllowListed && mode == AppOpsManager.MODE_IGNORED) { return AppUsageState.RESTRICTED; } else if (mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) { } else if (isAllowListed && mode == AppOpsManager.MODE_ALLOWED) { return AppUsageState.UNRESTRICTED; } else if (!mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) { } else if (!isAllowListed && mode == AppOpsManager.MODE_ALLOWED) { return AppUsageState.OPTIMIZED; } else { Log.d(TAG, "get unknown app usage state."); return AppUsageState.UNKNOWN; } } /** Gets the current {@link AppUsageState}. */ public AppUsageState getAppUsageState() { refreshState(); return getAppUsageState(mMode, mAllowListed); } public void setAppUsageState(AppUsageState state) { switch (state) { case RESTRICTED: Loading
tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +92 −0 Original line number Diff line number Diff line Loading @@ -27,11 +27,21 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.backup.BackupDataOutput; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.os.IDeviceIdleController; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import java.util.Arrays; import java.util.List; import org.junit.After; import org.junit.Before; Loading @@ -57,13 +67,23 @@ public final class BatteryBackupHelperTest { private BackupDataOutput mBackupDataOutput; @Mock private IDeviceIdleController mDeviceController; @Mock private IPackageManager mIPackageManager; @Mock private AppOpsManager mAppOpsManager; @Mock private UserManager mUserManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); mBatteryBackupHelper = new BatteryBackupHelper(mContext); mBatteryBackupHelper.mIDeviceIdleController = mDeviceController; mBatteryBackupHelper.mIPackageManager = mIPackageManager; } @After Loading Loading @@ -135,6 +155,78 @@ public final class BatteryBackupHelperTest { verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); } @Test public void backupOptimizationMode_nullInstalledApps_ignoreBackupOptimization() throws Exception { final UserInfo userInfo = new UserInfo(/*userId=*/ 0, /*userName=*/ "google", /*flag=*/ 0); doReturn(Arrays.asList(userInfo)).when(mUserManager).getProfiles(anyInt()); doThrow(new RuntimeException()) .when(mIPackageManager) .getInstalledApplications(anyInt(), anyInt()); mBatteryBackupHelper.backupOptimizationMode(mBackupDataOutput, null); verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt()); } @Test public void backupOptimizationMode_backupOptimizationMode() throws Exception { final String packageName1 = "com.android.testing.1"; final String packageName2 = "com.android.testing.2"; final String packageName3 = "com.android.testing.3"; final List<String> allowlistedApps = Arrays.asList(packageName1); createTestingData(packageName1, packageName2, packageName3); mBatteryBackupHelper.backupOptimizationMode(mBackupDataOutput, allowlistedApps); final String expectedResult = packageName1 + "|UNRESTRICTED," + packageName2 + "|RESTRICTED,"; final byte[] expectedBytes = expectedResult.getBytes(); verify(mBackupDataOutput).writeEntityHeader( BatteryBackupHelper.KEY_OPTIMIZATION_LIST, expectedBytes.length); verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); } private void createTestingData( String packageName1, String packageName2, String packageName3) throws Exception { // Sets the getInstalledApplications() method for testing. final UserInfo userInfo = new UserInfo(/*userId=*/ 0, /*userName=*/ "google", /*flag=*/ 0); doReturn(Arrays.asList(userInfo)).when(mUserManager).getProfiles(anyInt()); final ApplicationInfo applicationInfo1 = new ApplicationInfo(); applicationInfo1.enabled = true; applicationInfo1.uid = 1; applicationInfo1.packageName = packageName1; final ApplicationInfo applicationInfo2 = new ApplicationInfo(); applicationInfo2.enabled = false; applicationInfo2.uid = 2; applicationInfo2.packageName = packageName2; applicationInfo2.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; final ApplicationInfo applicationInfo3 = new ApplicationInfo(); applicationInfo3.enabled = false; applicationInfo3.uid = 3; applicationInfo3.packageName = packageName3; applicationInfo3.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED; doReturn(new ParceledListSlice<ApplicationInfo>( Arrays.asList(applicationInfo1, applicationInfo2, applicationInfo3))) .when(mIPackageManager) .getInstalledApplications(anyInt(), anyInt()); // Sets the AppOpsManager for checkOpNoThrow() method. doReturn(AppOpsManager.MODE_ALLOWED) .when(mAppOpsManager) .checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, applicationInfo1.uid, applicationInfo1.packageName); doReturn(AppOpsManager.MODE_IGNORED) .when(mAppOpsManager) .checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, applicationInfo2.uid, applicationInfo2.packageName); } @Implements(UserHandle.class) public static class ShadowUserHandle { // Sets the default as thte OWNER role. Loading