OSDN Git Service

Add functions for table action.
authorjackqdyulei <jackqdyulei@google.com>
Thu, 19 Jul 2018 17:09:47 +0000 (10:09 -0700)
committerjackqdyulei <jackqdyulei@google.com>
Thu, 26 Jul 2018 23:19:25 +0000 (16:19 -0700)
1. Insert/Update/Query/Delete
2. Update the action when app been restricted
3. Display restriction time in RestrictedAppDetails

Bug: 111366678
Test: RunSettingsRoboTests
Change-Id: I77a53f70e1ce4b612aabe28b7a1bb5df8f3ec9d5

res/values/strings.xml
src/com/android/settings/fuelgauge/BatteryUtils.java
src/com/android/settings/fuelgauge/RestrictedAppDetails.java
src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java
tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java
tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java

index 2bef192..2acf28b 100644 (file)
         <item quantity="other">Limiting battery usage for %1$d apps</item>
     </plurals>
 
+    <!-- Summary for restricted app to show the restriction time [CHAR LIMIT=NONE] -->
+    <string name="restricted_app_time_summary">Restricted <xliff:g id="time" example="5 days ago">%1$s</xliff:g></string>
+
     <!-- Footer message for restrict app details page -->
     <string name="restricted_app_detail_footer">These apps have been using battery in the background. Restricted apps may not work properly and notifications may be delayed.</string>
 
index 9e920c4..3676761 100644 (file)
@@ -38,11 +38,14 @@ import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.settings.R;
+import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
 import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
 import com.android.settings.fuelgauge.batterytip.StatsManagerConfig;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
 import com.android.settingslib.utils.PowerUtil;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -101,8 +104,8 @@ public class BatteryUtils {
         mContext = context;
         mPackageManager = context.getPackageManager();
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mPowerUsageFeatureProvider = FeatureFactory.getFactory(
-                context).getPowerUsageFeatureProvider(context);
+        mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
+                .getPowerUsageFeatureProvider(context);
     }
 
     public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
@@ -400,6 +403,18 @@ public class BatteryUtils {
         }
         // Control whether app could run jobs in the background
         mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
+
+        ThreadUtils.postOnBackgroundThread(() -> {
+            final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager
+                    .getInstance(mContext);
+            if (mode == AppOpsManager.MODE_IGNORED) {
+                batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION,
+                        uid, packageName, System.currentTimeMillis());
+            } else if (mode == AppOpsManager.MODE_ALLOWED) {
+                batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION,
+                        uid, packageName);
+            }
+        });
     }
 
     public boolean isForceAppStandbyEnabled(int uid, String packageName) {
index 4934bce..b64a707 100644 (file)
@@ -22,6 +22,8 @@ import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.SparseLongArray;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -29,7 +31,9 @@ import com.android.settings.Utils;
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
 import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
@@ -37,6 +41,7 @@ import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
 import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
 import com.android.settings.widget.AppCheckBoxPreference;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.StringUtil;
 import com.android.settingslib.widget.FooterPreferenceMixinCompat;
 
 import java.util.List;
@@ -57,6 +62,7 @@ public class RestrictedAppDetails extends DashboardFragment implements
     @VisibleForTesting
     static final String EXTRA_APP_INFO_LIST = "app_info_list";
     private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list";
+    private static final long TIME_NULL = -1;
 
     @VisibleForTesting
     List<AppInfo> mAppInfos;
@@ -68,6 +74,8 @@ public class RestrictedAppDetails extends DashboardFragment implements
     BatteryUtils mBatteryUtils;
     @VisibleForTesting
     PackageManager mPackageManager;
+    @VisibleForTesting
+    BatteryDatabaseManager mBatteryDatabaseManager;
     private final FooterPreferenceMixinCompat mFooterPreferenceMixin =
             new FooterPreferenceMixinCompat(this, getSettingsLifecycle());
 
@@ -96,6 +104,7 @@ public class RestrictedAppDetails extends DashboardFragment implements
         mPackageManager = context.getPackageManager();
         mIconDrawableFactory = IconDrawableFactory.newInstance(context);
         mBatteryUtils = BatteryUtils.getInstance(context);
+        mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context);
 
         refreshUi();
     }
@@ -135,6 +144,9 @@ public class RestrictedAppDetails extends DashboardFragment implements
     void refreshUi() {
         mRestrictedAppListGroup.removeAll();
         final Context context = getPrefContext();
+        final SparseLongArray timestampArray = mBatteryDatabaseManager
+                .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION);
+        final long now = System.currentTimeMillis();
 
         for (int i = 0, size = mAppInfos.size(); i < size; i++) {
             final CheckBoxPreference checkBoxPreference = new AppCheckBoxPreference(context);
@@ -158,9 +170,16 @@ public class RestrictedAppDetails extends DashboardFragment implements
 
                     return false;
                 });
+
+                final long timestamp = timestampArray.get(appInfo.uid, TIME_NULL);
+                if (timestamp != TIME_NULL) {
+                    checkBoxPreference.setSummary(getString(R.string.restricted_app_time_summary,
+                            StringUtil.formatRelativeTime(context, now - timestamp, false)));
+                }
+                final CharSequence test = checkBoxPreference.getSummaryOn();
                 mRestrictedAppListGroup.addPreference(checkBoxPreference);
             } catch (PackageManager.NameNotFoundException e) {
-                e.printStackTrace();
+                Log.e(TAG, "Can't find package: " + appInfo.packageName);
             }
         }
     }
index 910b368..513244e 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.settings.fuelgauge.batterytip;
 
 import static android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE;
+import static android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE;
+
 import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
         .ANOMALY_STATE;
 import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
@@ -26,6 +28,7 @@ import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.An
 import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
         .TIME_STAMP_MS;
 import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns.UID;
+import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.Tables.TABLE_ACTION;
 import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.Tables.TABLE_ANOMALY;
 
 import android.content.ContentValues;
@@ -34,12 +37,15 @@ import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.SparseLongArray;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.ActionColumns;
+
 import androidx.annotation.VisibleForTesting;
 
 /**
@@ -158,4 +164,65 @@ public class BatteryDatabaseManager {
             }
         }
     }
+
+    /**
+     * Query latest timestamps when an app has been performed action {@code type}
+     *
+     * @param type of action been performed
+     * @return {@link SparseLongArray} where key is uid and value is timestamp
+     */
+    public synchronized SparseLongArray queryActionTime(
+            @AnomalyDatabaseHelper.ActionType int type) {
+        final SparseLongArray timeStamps = new SparseLongArray();
+        try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) {
+            final String[] projection = {ActionColumns.UID, ActionColumns.TIME_STAMP_MS};
+            final String selection = ActionColumns.ACTION_TYPE + " = ? ";
+            final String[] selectionArgs = new String[]{String.valueOf(type)};
+
+            try (Cursor cursor = db.query(TABLE_ACTION, projection, selection, selectionArgs,
+                    null /* groupBy */, null /* having */, null /* orderBy */)) {
+                final int uidIndex = cursor.getColumnIndex(ActionColumns.UID);
+                final int timestampIndex = cursor.getColumnIndex(ActionColumns.TIME_STAMP_MS);
+
+                while (cursor.moveToNext()) {
+                    final int uid = cursor.getInt(uidIndex);
+                    final long timeStamp = cursor.getLong(timestampIndex);
+                    timeStamps.append(uid, timeStamp);
+                }
+            }
+        }
+
+        return timeStamps;
+    }
+
+    /**
+     * Insert an action, or update it if already existed
+     */
+    public synchronized boolean insertAction(@AnomalyDatabaseHelper.ActionType int type,
+            int uid, String packageName, long timestampMs) {
+        try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
+            final ContentValues values = new ContentValues();
+            values.put(ActionColumns.UID, uid);
+            values.put(ActionColumns.PACKAGE_NAME, packageName);
+            values.put(ActionColumns.ACTION_TYPE, type);
+            values.put(ActionColumns.TIME_STAMP_MS, timestampMs);
+            return db.insertWithOnConflict(TABLE_ACTION, null, values, CONFLICT_REPLACE) != -1;
+        }
+    }
+
+    /**
+     * Remove an action
+     */
+    public synchronized boolean deleteAction(@AnomalyDatabaseHelper.ActionType int type,
+            int uid, String packageName) {
+        try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
+            final String where =
+                    ActionColumns.ACTION_TYPE + " = ? AND " + ActionColumns.UID + " = ? AND "
+                            + ActionColumns.PACKAGE_NAME + " = ? ";
+            final String[] whereArgs = new String[]{String.valueOf(type), String.valueOf(uid),
+                    String.valueOf(packageName)};
+
+            return db.delete(TABLE_ACTION, where, whereArgs) != 0;
+        }
+    }
 }
index 58bfe0e..41e77e9 100644 (file)
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.spy;
 
 import android.content.Context;
 import android.text.format.DateUtils;
+import android.util.SparseLongArray;
 
 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
@@ -88,7 +89,7 @@ public class BatteryDatabaseManagerTest {
     }
 
     @Test
-    public void testAllFunctions() {
+    public void allAnomalyFunctions() {
         mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW,
                 AnomalyDatabaseHelper.State.NEW, NOW);
         mBatteryDatabaseManager.insertAnomaly(UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD,
@@ -113,7 +114,7 @@ public class BatteryDatabaseManagerTest {
     }
 
     @Test
-    public void testUpdateAnomalies_updateSuccessfully() {
+    public void updateAnomalies_updateSuccessfully() {
         mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW,
                 AnomalyDatabaseHelper.State.NEW, NOW);
         mBatteryDatabaseManager.insertAnomaly(UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD,
@@ -138,7 +139,7 @@ public class BatteryDatabaseManagerTest {
     }
 
     @Test
-    public void testQueryAnomalies_removeDuplicateByUid() {
+    public void queryAnomalies_removeDuplicateByUid() {
         mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW,
                 AnomalyDatabaseHelper.State.NEW, NOW);
         mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_OLD,
@@ -149,4 +150,28 @@ public class BatteryDatabaseManagerTest {
                 AnomalyDatabaseHelper.State.NEW);
         assertThat(newAppInfos).containsExactly(mCombinedAppInfo);
     }
+
+    @Test
+    public void allActionFunctions() {
+        final long timestamp = System.currentTimeMillis();
+        mBatteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_OLD,
+                PACKAGE_NAME_OLD, 0);
+        mBatteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_OLD,
+                PACKAGE_NAME_OLD, 1);
+        mBatteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_NEW,
+                PACKAGE_NAME_NEW, timestamp);
+
+        final SparseLongArray timeArray = mBatteryDatabaseManager.queryActionTime(
+                AnomalyDatabaseHelper.ActionType.RESTRICTION);
+        assertThat(timeArray.size()).isEqualTo(2);
+        assertThat(timeArray.get(UID_OLD)).isEqualTo(1);
+        assertThat(timeArray.get(UID_NEW)).isEqualTo(timestamp);
+
+        mBatteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_NEW,
+                PACKAGE_NAME_NEW);
+        final SparseLongArray recentTimeArray = mBatteryDatabaseManager.queryActionTime(
+                AnomalyDatabaseHelper.ActionType.RESTRICTION);
+        assertThat(recentTimeArray.size()).isEqualTo(1);
+        assertThat(timeArray.get(UID_OLD)).isEqualTo(1);
+    }
 }
index df34b78..b274492 100644 (file)
@@ -51,9 +51,12 @@ import android.text.format.DateUtils;
 
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
 import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowThreadUtils;
 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
 
 import org.junit.Before;
@@ -148,6 +151,8 @@ public class BatteryUtilsTest {
     private ApplicationInfo mLowApplicationInfo;
     @Mock
     private PowerWhitelistBackend mPowerWhitelistBackend;
+    @Mock
+    private BatteryDatabaseManager mBatteryDatabaseManager;
     private AnomalyInfo mAnomalyInfo;
     private BatteryUtils mBatteryUtils;
     private FakeFeatureFactory mFeatureFactory;
@@ -225,6 +230,8 @@ public class BatteryUtilsTest {
             .thenReturn(TOTAL_BATTERY_USAGE + BATTERY_SCREEN_USAGE);
         when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt()))
             .thenReturn(DISCHARGE_AMOUNT);
+        BatteryDatabaseManager.setUpForTest(mBatteryDatabaseManager);
+        ShadowThreadUtils.setIsMainThread(true);
     }
 
     @Test
@@ -570,6 +577,23 @@ public class BatteryUtilsTest {
     }
 
     @Test
+    public void testSetForceAppStandby_restrictApp_recordTime() {
+        mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+
+        verify(mBatteryDatabaseManager).insertAction(
+                eq(AnomalyDatabaseHelper.ActionType.RESTRICTION), eq(UID),
+                eq(HIGH_SDK_PACKAGE), anyLong());
+    }
+
+    @Test
+    public void testSetForceAppStandby_unrestrictApp_deleteTime() {
+        mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED);
+
+        verify(mBatteryDatabaseManager).deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION,
+                UID, HIGH_SDK_PACKAGE);
+    }
+
+    @Test
     public void testIsForceAppStandbyEnabled_enabled_returnTrue() {
         when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID,
                 PACKAGE_NAME)).thenReturn(AppOpsManager.MODE_IGNORED);
index b9ed509..7219f18 100644 (file)
@@ -28,10 +28,13 @@ import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.IconDrawableFactory;
+import android.util.SparseLongArray;
 
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
 import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
 import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
@@ -52,6 +55,7 @@ import org.robolectric.annotation.Config;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.CheckBoxPreference;
@@ -76,6 +80,8 @@ public class RestrictedAppDetailsTest {
     private IconDrawableFactory mIconDrawableFactory;
     @Mock
     private InstrumentedPreferenceFragment mFragment;
+    @Mock
+    private BatteryDatabaseManager mBatteryDatabaseManager;
     private PreferenceManager mPreferenceManager;
     private RestrictedAppDetails mRestrictedAppDetails;
     private Context mContext;
@@ -98,12 +104,14 @@ public class RestrictedAppDetailsTest {
 
         doReturn(mPreferenceManager).when(mRestrictedAppDetails).getPreferenceManager();
         doReturn(mContext).when(mFragment).getContext();
+        doReturn(mContext).when(mRestrictedAppDetails).getContext();
         mRestrictedAppDetails.mPackageManager = mPackageManager;
         mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory;
         mRestrictedAppDetails.mAppInfos = new ArrayList<>();
         mRestrictedAppDetails.mAppInfos.add(mAppInfo);
         mRestrictedAppDetails.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext));
         mRestrictedAppDetails.mBatteryUtils = spy(new BatteryUtils(mContext));
+        mRestrictedAppDetails.mBatteryDatabaseManager = mBatteryDatabaseManager;
         doReturn(mPreferenceManager).when(
                 mRestrictedAppDetails.mRestrictedAppListGroup).getPreferenceManager();
 
@@ -118,6 +126,10 @@ public class RestrictedAppDetailsTest {
         doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
         doReturn(true).when(mRestrictedAppDetails.mBatteryUtils).isForceAppStandbyEnabled(UID,
                 PACKAGE_NAME);
+        final SparseLongArray timestampArray = new SparseLongArray();
+        timestampArray.put(UID, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5));
+        doReturn(timestampArray).when(mBatteryDatabaseManager)
+                .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION);
 
         mRestrictedAppDetails.refreshUi();
 
@@ -126,6 +138,7 @@ public class RestrictedAppDetailsTest {
                 (CheckBoxPreference) mRestrictedAppDetails.mRestrictedAppListGroup.getPreference(0);
         assertThat(preference.getTitle()).isEqualTo(APP_NAME);
         assertThat(preference.isChecked()).isTrue();
+        assertThat(preference.getSummary()).isEqualTo("Restricted 5 hours ago");
     }
 
     @Test