Snap for 9853495 from d7a04c0467 to udc-release

Change-Id: Id274447130c30ed2be6cf9896b91796711226f01
This commit is contained in:
Android Build Coastguard Worker
2023-03-31 01:32:45 +00:00
36 changed files with 1158 additions and 221 deletions

View File

@@ -81,6 +81,7 @@ android_library {
"jsr305",
"net-utils-framework-common",
"app-usage-event-protos-lite",
"battery-event-protos-lite",
"settings-contextual-card-protos-lite",
"settings-log-bridge-protos-lite",
"settings-telephony-protos-lite",

View File

@@ -3110,6 +3110,7 @@
<intent-filter>
<action android:name="android.intent.action.BATTERY_LEVEL_CHANGED"/>
<action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_PLUGGING"/>
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"/>
</intent-filter>
</receiver>

View File

@@ -1565,6 +1565,22 @@
column="5"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;color name=&quot;screen_flash_color_button_outer_circle_stroke_color&quot;>#FFFFFF&lt;/color>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-night/colors.xml"
line="73"
column="5"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -3693,6 +3709,38 @@
column="9"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;#2E3192&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable-night/flash_notifications_illustration.xml"
line="37"
column="21"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;#000000&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable-night/flash_notifications_illustration.xml"
line="40"
column="21"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -4781,6 +4829,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_01_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -4861,6 +4925,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_02_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -4941,6 +5021,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_03_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5021,6 +5117,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_04_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5101,6 +5213,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_05_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5181,6 +5309,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_06_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5261,6 +5405,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_07_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5341,6 +5501,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_08_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5421,6 +5597,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_09_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5501,6 +5693,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_10_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5581,6 +5789,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_11_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -5661,6 +5885,22 @@
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:color=&quot;@color/screen_flash_color_button_outer_circle_stroke_color&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/screen_flash_color_12_checked_layer.xml"
line="41"
column="17"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"

View File

@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?androidprv:attr/colorAccentPrimaryVariant">
android:tint="?androidprv:attr/colorAccent">
<path
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"
android:fillColor="@android:color/white"/>

View File

@@ -15,7 +15,7 @@
-->
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyboard_check_icon"
android:id="@+id/check_icon"
android:src="@drawable/ic_check_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -30,6 +30,8 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
@@ -46,6 +48,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
OnPreferenceClickListener {
private static final String TAG = UsageAccessDetails.class.getSimpleName();
private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
@@ -65,6 +68,11 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
super.onCreate(savedInstanceState);
Context context = getActivity();
if (TextUtils.equals(mPackageName, context.getPackageName())) {
Log.w(TAG, "Unsupported app package.");
finish();
}
mUsageBridge = new AppStateUsageBridge(context, mState, null);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mDpm = context.getSystemService(DevicePolicyManager.class);

View File

@@ -19,6 +19,7 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.UserManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.text.BidiFormatter;
@@ -27,6 +28,8 @@ import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.style.RelativeSizeSpan;
import android.util.EventLog;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -84,6 +87,12 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Context context = getContext();
if (isGuestUser(context)) {
Log.e(TAG, "This setting isn't available due to user restriction.");
EventLog.writeEvent(0x534e4554, "262243574", -1 /* UID */, "Guest user");
finish();
return;
}
if (!isSimHardwareVisible(context) ||
MobileNetworkUtils.isMobileNetworkUserRestricted(context)) {
@@ -285,4 +294,11 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage
updateState();
mSummaryController.updateState(mSummaryPreference);
}
private static boolean isGuestUser(Context context) {
if (context == null) return false;
final UserManager userManager = context.getSystemService(UserManager.class);
if (userManager == null) return false;
return userManager.isGuestUser();
}
}

View File

@@ -22,9 +22,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import android.widget.Switch;
import androidx.preference.PreferenceScreen;
@@ -36,15 +34,13 @@ import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.widget.MainSwitchPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
/**
* Controller to update the battery saver button
*/
public class BatterySaverButtonPreferenceController extends
TogglePreferenceController implements OnMainSwitchChangeListener, LifecycleObserver,
OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
TogglePreferenceController implements LifecycleObserver, OnStart, OnStop,
BatterySaverReceiver.BatterySaverListener {
private static final long SWITCH_ANIMATION_DURATION = 350L;
private final BatterySaverReceiver mBatterySaverReceiver;
@@ -96,20 +92,9 @@ public class BatterySaverButtonPreferenceController extends
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
mPreference.addOnSwitchChangeListener(this);
mPreference.updateStatus(isChecked());
}
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
// Cancel preference's check state once it's first time launch
if (isChecked && (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) == 0)) {
mPreference.setChecked(false);
}
setChecked(isChecked);
}
@Override
public boolean isChecked() {
return mPowerManager.isPowerSaveMode();

View File

@@ -30,6 +30,8 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.fuelgauge.BatteryStatus;
import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
@@ -37,7 +39,10 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
/** An intent action to request Settings to clear cache data. */
public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
"com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
/** An intent action to request Settings to clear cache data. */
/** An intent action for power is plugging. */
public static final String ACTION_BATTERY_PLUGGING =
"com.android.settings.battery.action.ACTION_BATTERY_PLUGGING";
/** An intent action for power is unplugging. */
public static final String ACTION_BATTERY_UNPLUGGING =
"com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING";
@@ -49,6 +54,8 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
@VisibleForTesting
boolean mFetchBatteryUsageData = false;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null || intent.getAction() == null) {
@@ -70,7 +77,11 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
tryToFetchUsageData(context);
}
break;
case ACTION_BATTERY_PLUGGING:
sendBatteryEventData(context, BatteryEventType.POWER_CONNECTED);
break;
case ACTION_BATTERY_UNPLUGGING:
sendBatteryEventData(context, BatteryEventType.POWER_DISCONNECTED);
// Only when fullChargeIntentAction is ACTION_POWER_DISCONNECTED,
// ACTION_BATTERY_UNPLUGGING will be considered as the full charge event and then
// start usage events fetching.
@@ -106,4 +117,12 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
mFetchBatteryUsageData = true;
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ true);
}
private void sendBatteryEventData(Context context, BatteryEventType batteryEventType) {
final long timestamp = System.currentTimeMillis();
final Intent intent = BatteryUtils.getBatteryIntent(context);
final int batteryLevel = BatteryStatus.getBatteryLevel(intent);
mExecutor.execute(() -> DatabaseUtils.sendBatteryEventData(context,
ConvertUtils.convertToBatteryEvent(timestamp, batteryEventType, batteryLevel)));
}
}

View File

@@ -31,6 +31,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
@@ -52,6 +54,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
private static final int BATTERY_STATE_CODE = 1;
private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2;
private static final int APP_USAGE_EVENT_CODE = 3;
private static final int BATTERY_EVENT_CODE = 4;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
@@ -68,11 +71,16 @@ public class BatteryUsageContentProvider extends ContentProvider {
DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE,
/*code=*/ APP_USAGE_EVENT_CODE);
sUriMatcher.addURI(
DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_EVENT_TABLE,
/*code=*/ BATTERY_EVENT_CODE);
}
private Clock mClock;
private BatteryStateDao mBatteryStateDao;
private AppUsageEventDao mAppUsageEventDao;
private BatteryEventDao mBatteryEventDao;
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public void setClock(Clock clock) {
@@ -88,6 +96,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
mClock = Clock.systemUTC();
mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao();
mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao();
mBatteryEventDao = BatteryStateDatabase.getInstance(getContext()).batteryEventDao();
Log.w(TAG, "create content provider from " + getCallingPackage());
return true;
}
@@ -107,6 +116,8 @@ public class BatteryUsageContentProvider extends ContentProvider {
return getAppUsageEvents(uri);
case APP_USAGE_LATEST_TIMESTAMP_CODE:
return getAppUsageLatestTimestamp(uri);
case BATTERY_EVENT_CODE:
return getBatteryEvents(uri);
default:
throw new IllegalArgumentException("unknown URI: " + uri);
}
@@ -138,6 +149,14 @@ public class BatteryUsageContentProvider extends ContentProvider {
Log.e(TAG, "insert() from:" + uri + " error:" + e);
return null;
}
case BATTERY_EVENT_CODE:
try {
mBatteryEventDao.insert(BatteryEventEntity.create(contentValues));
return uri;
} catch (RuntimeException e) {
Log.e(TAG, "insert() from:" + uri + " error:" + e);
return null;
}
default:
throw new IllegalArgumentException("unknown URI: " + uri);
}
@@ -190,7 +209,6 @@ public class BatteryUsageContentProvider extends ContentProvider {
}
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
return cursor;
}
private Cursor getAppUsageLatestTimestamp(Uri uri) {
@@ -210,6 +228,19 @@ public class BatteryUsageContentProvider extends ContentProvider {
return cursor;
}
private Cursor getBatteryEvents(Uri uri) {
final long queryTimestamp = getQueryTimestamp(uri);
final long timestamp = mClock.millis();
Cursor cursor = null;
try {
cursor = mBatteryEventDao.getAllAfter(queryTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:" + e);
}
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
return cursor;
}
// If URI contains query parameter QUERY_KEY_USERID, use the value directly.
// Otherwise, return null.
private List<Long> getQueryUserIds(Uri uri) {

View File

@@ -38,6 +38,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -125,6 +126,15 @@ public final class ConvertUtils {
return values;
}
/** Converts {@link BatteryEvent} to content values */
public static ContentValues convertBatteryEventToContentValues(final BatteryEvent event) {
final ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, event.getTimestamp());
values.put(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE, event.getType().getNumber());
values.put(BatteryEventEntity.KEY_BATTERY_LEVEL, event.getBatteryLevel());
return values;
}
/** Gets the encoded string from {@link BatteryInformation} instance. */
public static String convertBatteryInformationToString(
final BatteryInformation batteryInformation) {
@@ -237,6 +247,29 @@ public final class ConvertUtils {
return eventBuilder.build();
}
/** Converts to {@link BatteryEvent} from {@link BatteryEventType} */
public static BatteryEvent convertToBatteryEvent(
long timestamp, BatteryEventType type, int batteryLevel) {
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
eventBuilder.setTimestamp(timestamp);
eventBuilder.setType(type);
eventBuilder.setBatteryLevel(batteryLevel);
return eventBuilder.build();
}
/** Converts to {@link BatteryEvent} from {@link Cursor} */
public static BatteryEvent convertToBatteryEventFromCursor(final Cursor cursor) {
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
eventBuilder.setTimestamp(getLongFromCursor(cursor, BatteryEventEntity.KEY_TIMESTAMP));
eventBuilder.setType(
BatteryEventType.forNumber(
getIntegerFromCursor(
cursor, BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)));
eventBuilder.setBatteryLevel(
getIntegerFromCursor(cursor, BatteryEventEntity.KEY_BATTERY_LEVEL));
return eventBuilder.build();
}
/** Converts UTC timestamp to local time string for logging only, so use the US locale for
* better readability in debugging. */
public static String utcToLocalTimeForLogging(long timestamp) {

View File

@@ -72,6 +72,8 @@ public class DataProcessManager {
private final Handler mHandler;
private final DataProcessor.UsageMapAsyncResponse mCallbackFunction;
private final List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
private final List<BatteryEvent> mBatteryEventList = new ArrayList<>();
private Context mContext;
private UserManager mUserManager;
@@ -84,12 +86,12 @@ public class DataProcessManager {
private boolean mIsCurrentBatteryHistoryLoaded = false;
private boolean mIsCurrentAppUsageLoaded = false;
private boolean mIsDatabaseAppUsageLoaded = false;
private boolean mIsBatteryEventLoaded = false;
// Used to identify whether screen-on time data should be shown in the UI.
private boolean mShowScreenOnTime = true;
// Used to identify whether battery level data should be shown in the UI.
private boolean mShowBatteryLevel = true;
private List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
/**
* The indexed {@link AppUsagePeriod} list data for each corresponding time slot.
* <p>{@code Long} stands for the userId.</p>
@@ -146,6 +148,8 @@ public class DataProcessManager {
loadDatabaseAppUsageList();
// Loads the latest app usage list from the service.
loadCurrentAppUsageList();
// Loads the battery event list from database.
loadBatteryEventList();
} else {
// If there is no battery level data, only load the battery history data from service
// and show it as the app list directly.
@@ -174,6 +178,11 @@ public class DataProcessManager {
return mIsDatabaseAppUsageLoaded;
}
@VisibleForTesting
boolean getIsBatteryEventLoaded() {
return mIsBatteryEventLoaded;
}
@VisibleForTesting
boolean getIsCurrentBatteryHistoryLoaded() {
return mIsCurrentBatteryHistoryLoaded;
@@ -290,7 +299,7 @@ public class DataProcessManager {
return null;
}
final long startTime = System.currentTimeMillis();
// Loads the current battery usage data from the battery stats service.
// Loads the app usage data from the database.
final List<AppUsageEvent> appUsageEventList =
DatabaseUtils.getAppUsageEventForUsers(
mContext, Calendar.getInstance(), getCurrentUserIds(),
@@ -314,6 +323,35 @@ public class DataProcessManager {
}.execute();
}
private void loadBatteryEventList() {
new AsyncTask<Void, Void, List<BatteryEvent>>() {
@Override
protected List<BatteryEvent> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
// Loads the battery event data from the database.
final List<BatteryEvent> batteryEventList =
DatabaseUtils.getBatteryEvents(
mContext, Calendar.getInstance(), mRawStartTimestamp);
Log.d(TAG, String.format("execute loadBatteryEventList size=%d in %d/ms",
batteryEventList.size(), (System.currentTimeMillis() - startTime)));
return batteryEventList;
}
@Override
protected void onPostExecute(
final List<BatteryEvent> batteryEventList) {
if (batteryEventList == null || batteryEventList.isEmpty()) {
Log.d(TAG, "batteryEventList is null or empty");
} else {
mBatteryEventList.clear();
mBatteryEventList.addAll(batteryEventList);
}
mIsBatteryEventLoaded = true;
tryToProcessAppUsageData();
}
}.execute();
}
private void loadAndApplyBatteryMapFromServiceOnly() {
new AsyncTask<Void, Void, BatteryCallbackData>() {
@Override
@@ -344,9 +382,8 @@ public class DataProcessManager {
}
private void tryToProcessAppUsageData() {
// Only when all app usage events has been loaded, start processing app usage data to an
// intermediate result for further use.
if (!mIsCurrentAppUsageLoaded || !mIsDatabaseAppUsageLoaded) {
// Ignore processing the data if any required data is not loaded.
if (!mIsCurrentAppUsageLoaded || !mIsDatabaseAppUsageLoaded || !mIsBatteryEventLoaded) {
return;
}
processAppUsageData();
@@ -360,16 +397,16 @@ public class DataProcessManager {
}
// Generates the indexed AppUsagePeriod list data for each corresponding time slot for
// further use.
mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(
mRawStartTimestamp, mHourlyBatteryLevelsPerDay, mAppUsageEventList);
mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(mRawStartTimestamp,
mHourlyBatteryLevelsPerDay, mAppUsageEventList, mBatteryEventList);
}
private void tryToGenerateFinalDataAndApplyCallback() {
// Only when both battery history data and app usage events data has been loaded, start the
// final data processing.
// Ignore processing the data if any required data is not loaded.
if (!mIsCurrentBatteryHistoryLoaded
|| !mIsCurrentAppUsageLoaded
|| !mIsDatabaseAppUsageLoaded) {
|| !mIsDatabaseAppUsageLoaded
|| !mIsBatteryEventLoaded) {
return;
}
generateFinalDataAndApplyCallback();

View File

@@ -94,6 +94,14 @@ public final class DataProcessor {
@VisibleForTesting
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
static final Comparator<AppUsageEvent> APP_USAGE_EVENT_TIMESTAMP_COMPARATOR =
Comparator.comparing(AppUsageEvent::getTimestamp);
@VisibleForTesting
static final Comparator<BatteryEvent> BATTERY_EVENT_TIMESTAMP_COMPARATOR =
Comparator.comparing(BatteryEvent::getTimestamp);
@VisibleForTesting
static boolean sDebug = false;
@@ -110,8 +118,6 @@ public final class DataProcessor {
public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
public static final Comparator<AppUsageEvent> TIMESTAMP_COMPARATOR =
Comparator.comparing(AppUsageEvent::getTimestamp);
/** A callback listener when battery usage loading async task is executed. */
public interface UsageMapAsyncResponse {
@@ -266,14 +272,16 @@ public final class DataProcessor {
generateAppUsagePeriodMap(
final long rawStartTimestamp,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final List<AppUsageEvent> appUsageEventList) {
final List<AppUsageEvent> appUsageEventList,
final List<BatteryEvent> batteryEventList) {
if (appUsageEventList.isEmpty()) {
Log.w(TAG, "appUsageEventList is empty");
return null;
}
// Sorts the appUsageEventList in ascending order based on the timestamp before
// distribution.
Collections.sort(appUsageEventList, TIMESTAMP_COMPARATOR);
// Sorts the appUsageEventList and batteryEventList in ascending order based on the
// timestamp before distribution.
Collections.sort(appUsageEventList, APP_USAGE_EVENT_TIMESTAMP_COMPARATOR);
Collections.sort(batteryEventList, BATTERY_EVENT_TIMESTAMP_COMPARATOR);
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> resultMap =
new ArrayMap<>();
@@ -309,8 +317,8 @@ public final class DataProcessor {
// The value could be null when there is no data in the hourly slot.
dailyMap.put(
hourlyIndex,
buildAppUsagePeriodList(
hourlyAppUsageEventList, startTimestamp, endTimestamp));
buildAppUsagePeriodList(hourlyAppUsageEventList, batteryEventList,
startTimestamp, endTimestamp));
}
}
return resultMap;
@@ -717,7 +725,8 @@ public final class DataProcessor {
@VisibleForTesting
@Nullable
static Map<Long, Map<String, List<AppUsagePeriod>>> buildAppUsagePeriodList(
final List<AppUsageEvent> allAppUsageEvents, final long startTime, final long endTime) {
final List<AppUsageEvent> allAppUsageEvents, final List<BatteryEvent> batteryEventList,
final long startTime, final long endTime) {
if (allAppUsageEvents.isEmpty()) {
return null;
}
@@ -763,12 +772,14 @@ public final class DataProcessor {
usageEvents.addAll(deviceEvents);
// Sorts the usageEvents in ascending order based on the timestamp before computing the
// period.
Collections.sort(usageEvents, TIMESTAMP_COMPARATOR);
Collections.sort(usageEvents, APP_USAGE_EVENT_TIMESTAMP_COMPARATOR);
// A package might have multiple instances. Computes the usage period per instance id
// and then merges them into the same user-package map.
final List<AppUsagePeriod> usagePeriodList =
buildAppUsagePeriodListPerInstance(usageEvents, startTime, endTime);
excludePowerConnectedTimeFromAppUsagePeriodList(
buildAppUsagePeriodListPerInstance(usageEvents, startTime, endTime),
batteryEventList);
if (!usagePeriodList.isEmpty()) {
addToUsagePeriodMap(allUsagePeriods, usagePeriodList, eventUserId, packageName);
}
@@ -836,6 +847,53 @@ public final class DataProcessor {
return usagePeriodList;
}
@VisibleForTesting
static List<AppUsagePeriod> excludePowerConnectedTimeFromAppUsagePeriodList(
final List<AppUsagePeriod> usagePeriodList,
final List<BatteryEvent> batteryEventList) {
final List<AppUsagePeriod> resultList = new ArrayList<>();
for (AppUsagePeriod inputPeriod : usagePeriodList) {
long lastStartTime = inputPeriod.getStartTime();
for (BatteryEvent batteryEvent : batteryEventList) {
if (batteryEvent.getTimestamp() < inputPeriod.getStartTime()) {
// Because the batteryEventList has been sorted, here is to mark the power
// connection state when the usage period starts. If power is connected when
// the usage period starts, the starting period will be ignored; otherwise it
// will be added.
if (batteryEvent.getType() == BatteryEventType.POWER_CONNECTED) {
lastStartTime = 0;
} else if (batteryEvent.getType() == BatteryEventType.POWER_DISCONNECTED) {
lastStartTime = inputPeriod.getStartTime();
}
continue;
}
if (batteryEvent.getTimestamp() > inputPeriod.getEndTime()) {
// Because the batteryEventList has been sorted, if any event is already after
// the end time, all the following events should be able to drop directly.
break;
}
if (batteryEvent.getType() == BatteryEventType.POWER_CONNECTED
&& lastStartTime != 0) {
resultList.add(AppUsagePeriod.newBuilder()
.setStartTime(lastStartTime)
.setEndTime(batteryEvent.getTimestamp())
.build());
lastStartTime = 0;
} else if (batteryEvent.getType() == BatteryEventType.POWER_DISCONNECTED) {
lastStartTime = batteryEvent.getTimestamp();
}
}
if (lastStartTime != 0) {
resultList.add(AppUsagePeriod.newBuilder()
.setStartTime(lastStartTime)
.setEndTime(inputPeriod.getEndTime())
.build());
}
}
return resultList;
}
@VisibleForTesting
static long getScreenOnTime(
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsageMap,

View File

@@ -64,10 +64,12 @@ public final class DatabaseUtils {
/** An authority name of the battery content provider. */
public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
/** A table name for battery usage history. */
public static final String BATTERY_STATE_TABLE = "BatteryState";
/** A table name for app usage events. */
public static final String APP_USAGE_EVENT_TABLE = "AppUsageEvent";
/** A table name for battery events. */
public static final String BATTERY_EVENT_TABLE = "BatteryEvent";
/** A table name for battery usage history. */
public static final String BATTERY_STATE_TABLE = "BatteryState";
/** A path name for app usage latest timestamp query. */
public static final String APP_USAGE_LATEST_TIMESTAMP_PATH = "appUsageLatestTimestamp";
/** A class name for battery usage data provider. */
@@ -84,13 +86,6 @@ public final class DatabaseUtils {
*/
public static final long USAGE_QUERY_BUFFER_HOURS = Duration.ofHours(3).toMillis();
/** A content URI to access battery usage states data. */
public static final Uri BATTERY_CONTENT_URI =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_STATE_TABLE)
.build();
/** A content URI to access app usage events data. */
public static final Uri APP_USAGE_EVENT_URI =
new Uri.Builder()
@@ -98,6 +93,20 @@ public final class DatabaseUtils {
.authority(AUTHORITY)
.appendPath(APP_USAGE_EVENT_TABLE)
.build();
/** A content URI to access battery events data. */
public static final Uri BATTERY_EVENT_URI =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_EVENT_TABLE)
.build();
/** A content URI to access battery usage states data. */
public static final Uri BATTERY_CONTENT_URI =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_STATE_TABLE)
.build();
// For testing only.
@VisibleForTesting
@@ -106,6 +115,8 @@ public final class DatabaseUtils {
static Supplier<Cursor> sFakeAppUsageEventSupplier;
@VisibleForTesting
static Supplier<Cursor> sFakeAppUsageLatestTimestampSupplier;
@VisibleForTesting
static Supplier<Cursor> sFakeBatteryEventSupplier;
private DatabaseUtils() {
}
@@ -176,6 +187,32 @@ public final class DatabaseUtils {
return appUsageEventList;
}
/** Returns the battery event data since the query timestamp in battery event table. */
public static List<BatteryEvent> getBatteryEvents(
Context context,
final Calendar calendar,
final long rawStartTimestamp) {
final long startTime = System.currentTimeMillis();
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
final long queryTimestamp = Math.max(rawStartTimestamp, sixDaysAgoTimestamp);
Log.d(TAG, "getBatteryEvents for timestamp: " + queryTimestamp);
// Builds the content uri everytime to avoid cache.
final Uri batteryEventUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_EVENT_TABLE)
.appendQueryParameter(
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.build();
final List<BatteryEvent> batteryEventList =
loadBatteryEventsFromContentProvider(context, batteryEventUri);
Log.d(TAG, String.format("getBatteryEvents size=%d in %d/ms", batteryEventList.size(),
(System.currentTimeMillis() - startTime)));
return batteryEventList;
}
/** Long: for timestamp and String: for BatteryHistEntry.getKey() */
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
Context context, Calendar calendar) {
@@ -210,8 +247,9 @@ public final class DatabaseUtils {
try {
final BatteryStateDatabase database = BatteryStateDatabase
.getInstance(context.getApplicationContext());
database.batteryStateDao().clearAll();
database.appUsageEventDao().clearAll();
database.batteryEventDao().clearAll();
database.batteryStateDao().clearAll();
} catch (RuntimeException e) {
Log.e(TAG, "clearAll() failed", e);
}
@@ -226,8 +264,9 @@ public final class DatabaseUtils {
.getInstance(context.getApplicationContext());
final long earliestTimestamp = Clock.systemUTC().millis()
- Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis();
database.batteryStateDao().clearAllBefore(earliestTimestamp);
database.appUsageEventDao().clearAllBefore(earliestTimestamp);
database.batteryEventDao().clearAllBefore(earliestTimestamp);
database.batteryStateDao().clearAllBefore(earliestTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "clearAllBefore() failed", e);
}
@@ -292,6 +331,23 @@ public final class DatabaseUtils {
return valuesList;
}
static ContentValues sendBatteryEventData(
final Context context, final BatteryEvent batteryEvent) {
final long startTime = System.currentTimeMillis();
ContentValues contentValues = ConvertUtils.convertBatteryEventToContentValues(batteryEvent);
final ContentResolver resolver = context.getContentResolver();
try {
resolver.insert(BATTERY_EVENT_URI, contentValues);
Log.d(TAG, "insert() battery event data into database: " + batteryEvent.toString());
} catch (Exception e) {
Log.e(TAG, "insert() battery event data into database error:\n" + e);
}
Log.d(TAG, String.format("sendBatteryEventData() in %d/ms",
(System.currentTimeMillis() - startTime)));
clearMemory();
return contentValues;
}
static List<ContentValues> sendBatteryEntryData(
final Context context,
final List<BatteryEntry> batteryEntryList,
@@ -392,6 +448,8 @@ public final class DatabaseUtils {
public static void dump(Context context, PrintWriter writer) {
writeString(context, writer, "BatteryLevelChanged",
Intent.ACTION_BATTERY_LEVEL_CHANGED);
writeString(context, writer, "BatteryPlugging",
BatteryUsageBroadcastReceiver.ACTION_BATTERY_PLUGGING);
writeString(context, writer, "BatteryUnplugging",
BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING);
writeString(context, writer, "ClearBatteryCacheData",
@@ -475,6 +533,32 @@ public final class DatabaseUtils {
return appUsageEventList;
}
private static List<BatteryEvent> loadBatteryEventsFromContentProvider(
Context context, Uri batteryEventUri) {
final List<BatteryEvent> batteryEventList = new ArrayList<>();
context = getParentContext(context);
if (context == null) {
return batteryEventList;
}
try (Cursor cursor = sFakeBatteryEventSupplier != null
? sFakeBatteryEventSupplier.get()
: context.getContentResolver().query(batteryEventUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return batteryEventList;
}
// Loads and recovers all AppUsageEvent data from cursor.
while (cursor.moveToNext()) {
batteryEventList.add(ConvertUtils.convertToBatteryEventFromCursor(cursor));
}
try {
cursor.close();
} catch (Exception e) {
Log.e(TAG, "cursor.close() failed", e);
}
}
return batteryEventList;
}
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
Context context, Uri batteryStateUri) {
context = DatabaseUtils.getParentContext(context);

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import android.database.Cursor;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
/** Data access object for accessing {@link BatteryEventEntity} in the database. */
@Dao
public interface BatteryEventDao {
/** Inserts a {@link BatteryEventEntity} data into the database. */
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(BatteryEventEntity event);
/** Gets all recorded data. */
@Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC")
List<BatteryEventEntity> getAll();
/** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
Cursor getAllAfter(long timestamp);
/** Deletes all recorded data before a specific timestamp. */
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
void clearAllBefore(long timestamp);
/** Clears all recorded data in the database. */
@Query("DELETE FROM BatteryEventEntity")
void clearAll();
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import android.content.ContentValues;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Locale;
/** A {@link Entity} class to save battery events into database. */
@Entity
public class BatteryEventEntity {
/** Keys for accessing {@link ContentValues}. */
public static final String KEY_TIMESTAMP = "timestamp";
public static final String KEY_BATTERY_EVENT_TYPE = "batteryEventType";
public static final String KEY_BATTERY_LEVEL = "batteryLevel";
@PrimaryKey(autoGenerate = true)
private long mId;
public final long timestamp;
public final int batteryEventType;
public final int batteryLevel;
public BatteryEventEntity(
final long timestamp,
final int batteryEventType,
final int batteryLevel) {
this.timestamp = timestamp;
this.batteryEventType = batteryEventType;
this.batteryLevel = batteryLevel;
}
/** Sets the auto-generated content ID. */
public void setId(long id) {
this.mId = id;
}
/** Gets the auto-generated content ID. */
public long getId() {
return mId;
}
@Override
public String toString() {
final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(timestamp);
final StringBuilder builder = new StringBuilder()
.append("\nBatteryEvent{")
.append(String.format(Locale.US,
"\n\ttimestamp=%s|batteryEventType=%d|batteryLevel=%d",
recordAtDateTime, batteryEventType, batteryLevel))
.append("\n}");
return builder.toString();
}
/** Creates new {@link BatteryEventEntity} from {@link ContentValues}. */
public static BatteryEventEntity create(ContentValues contentValues) {
Builder builder = BatteryEventEntity.newBuilder();
if (contentValues.containsKey(KEY_TIMESTAMP)) {
builder.setTimestamp(contentValues.getAsLong(KEY_TIMESTAMP));
}
if (contentValues.containsKey(KEY_BATTERY_EVENT_TYPE)) {
builder.setBatteryEventType(contentValues.getAsInteger(KEY_BATTERY_EVENT_TYPE));
}
if (contentValues.containsKey(KEY_BATTERY_LEVEL)) {
builder.setBatteryLevel(contentValues.getAsInteger(KEY_BATTERY_LEVEL));
}
return builder.build();
}
/** Creates a new {@link Builder} instance. */
public static Builder newBuilder() {
return new Builder();
}
/** A convenience builder class to improve readability. */
public static class Builder {
private long mTimestamp;
private int mBatteryEventType;
private int mBatteryLevel;
/** Sets the timestamp. */
@CanIgnoreReturnValue
public Builder setTimestamp(final long timestamp) {
mTimestamp = timestamp;
return this;
}
/** Sets the battery event type. */
@CanIgnoreReturnValue
public Builder setBatteryEventType(final int batteryEventType) {
mBatteryEventType = batteryEventType;
return this;
}
/** Sets the battery level. */
@CanIgnoreReturnValue
public Builder setBatteryLevel(final int batteryLevel) {
mBatteryLevel = batteryLevel;
return this;
}
/** Builds the {@link BatteryEventEntity}. */
public BatteryEventEntity build() {
return new BatteryEventEntity(
mTimestamp,
mBatteryEventType,
mBatteryLevel);
}
private Builder() {}
}
}

View File

@@ -25,24 +25,26 @@ import androidx.room.RoomDatabase;
/** A {@link RoomDatabase} for battery usage states history. */
@Database(
entities = {BatteryState.class, AppUsageEventEntity.class},
entities = {AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class},
version = 1)
public abstract class BatteryStateDatabase extends RoomDatabase {
private static final String TAG = "BatteryStateDatabase";
private static BatteryStateDatabase sBatteryStateDatabase;
/** Provides DAO for battery state table. */
public abstract BatteryStateDao batteryStateDao();
/** Provides DAO for app usage event table. */
public abstract AppUsageEventDao appUsageEventDao();
/** Provides DAO for battery event table. */
public abstract BatteryEventDao batteryEventDao();
/** Provides DAO for battery state table. */
public abstract BatteryStateDao batteryStateDao();
/** Gets or creates an instance of {@link RoomDatabase}. */
public static BatteryStateDatabase getInstance(Context context) {
if (sBatteryStateDatabase == null) {
sBatteryStateDatabase =
Room.databaseBuilder(
context, BatteryStateDatabase.class, "battery-usage-db-v7")
context, BatteryStateDatabase.class, "battery-usage-db-v8")
// Allows accessing data in the main thread for dumping bugreport.
.allowMainThreadQueries()
.fallbackToDestructiveMigration()

View File

@@ -7,6 +7,22 @@ package {
default_applicable_licenses: ["packages_apps_Settings_license"],
}
java_library {
name: "app-usage-event-protos-lite",
proto: {
type: "lite",
},
srcs: ["app_usage_event.proto"],
}
java_library {
name: "battery-event-protos-lite",
proto: {
type: "lite",
},
srcs: ["battery_event.proto"],
}
java_library {
name: "fuelgauge-usage-state-protos-lite",
proto: {
@@ -14,11 +30,3 @@ java_library {
},
srcs: ["fuelgauge_usage_state.proto"],
}
java_library {
name: "app-usage-event-protos-lite",
proto: {
type: "lite",
},
srcs: ["app_usage_event.proto"],
}

View File

@@ -0,0 +1,17 @@
syntax = "proto2";
option java_multiple_files = true;
option java_package = "com.android.settings.fuelgauge.batteryusage";
option java_outer_classname = "BatteryEventProto";
enum BatteryEventType {
UNKNOWN_EVENT = 0;
POWER_CONNECTED = 1;
POWER_DISCONNECTED = 2;
}
message BatteryEvent {
optional int64 timestamp = 1;
optional BatteryEventType type = 2;
optional int32 battery_level = 3;
}

View File

@@ -28,6 +28,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.widget.TickButtonPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -38,7 +39,7 @@ import java.util.Map;
public class NewKeyboardLayoutPickerController extends BasePreferenceController implements
InputManager.InputDeviceListener, LifecycleObserver, OnStart, OnStop {
private final InputManager mIm;
private final Map<KeyboardLayoutPreference, KeyboardLayout> mPreferenceMap;
private final Map<TickButtonPreference, KeyboardLayout> mPreferenceMap;
private Fragment mParent;
private int mInputDeviceId;
@@ -102,16 +103,15 @@ public class NewKeyboardLayoutPickerController extends BasePreferenceController
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!(preference instanceof KeyboardLayoutPreference)) {
if (!(preference instanceof TickButtonPreference)) {
return false;
}
final KeyboardLayoutPreference pref = (KeyboardLayoutPreference) preference;
pref.setCheckMark(true);
final TickButtonPreference pref = (TickButtonPreference) preference;
pref.setSelected(true);
if (mPreviousSelection != null && !mPreviousSelection.equals(preference.getKey())) {
KeyboardLayoutPreference preSelectedPref = mScreen.findPreference(mPreviousSelection);
preSelectedPref.setCheckMark(false);
TickButtonPreference preSelectedPref = mScreen.findPreference(mPreviousSelection);
preSelectedPref.setSelected(false);
}
setLayout(pref);
mPreviousSelection = preference.getKey();
@@ -140,12 +140,13 @@ public class NewKeyboardLayoutPickerController extends BasePreferenceController
return;
}
for (KeyboardLayout layout : mKeyboardLayouts) {
final KeyboardLayoutPreference pref;
final TickButtonPreference pref;
pref = new TickButtonPreference(mScreen.getContext());
pref.setTitle(layout.getLabel());
if (mLayout.equals(layout.getLabel())) {
pref = new KeyboardLayoutPreference(mScreen.getContext(), layout.getLabel(), true);
pref.setSelected(true);
mPreviousSelection = layout.getDescriptor();
} else {
pref = new KeyboardLayoutPreference(mScreen.getContext(), layout.getLabel(), false);
}
pref.setKey(layout.getDescriptor());
mScreen.addPreference(pref);
@@ -153,7 +154,7 @@ public class NewKeyboardLayoutPickerController extends BasePreferenceController
}
}
private void setLayout(KeyboardLayoutPreference preference) {
private void setLayout(TickButtonPreference preference) {
mIm.setKeyboardLayoutForInputDevice(
mInputDeviceIdentifier,
mUserId,

View File

@@ -34,6 +34,7 @@ import com.android.internal.app.LocaleStore;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.TickButtonPreference;
import java.util.Locale;
@@ -138,7 +139,7 @@ public class NumberingSystemItemController extends BasePreferenceController {
String key = supportedLocale.getUnicodeLocaleType(
ExtensionTypes.NUMBERING_SYSTEM);
pref.setKey(key == null ? RegionalPreferencesDataUtils.DEFAULT_VALUE : key);
pref.setTickEnable(isSameNumberingSystem(targetLocale, supportedLocale));
pref.setSelected(isSameNumberingSystem(targetLocale, supportedLocale));
screen.addPreference(pref);
}
}
@@ -162,7 +163,7 @@ public class NumberingSystemItemController extends BasePreferenceController {
TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(i);
Log.i(TAG, "[onPreferenceClick] key is " + pref.getKey());
if (pref.getKey().equals(preference.getKey())) {
pref.setTickEnable(true);
pref.setSelected(true);
Locale updatedLocale =
saveNumberingSystemToLocale(
Locale.forLanguageTag(mSelectedLanguage), pref.getKey());
@@ -176,7 +177,7 @@ public class NumberingSystemItemController extends BasePreferenceController {
mParentFragment.setArguments(bundle);
continue;
}
pref.setTickEnable(false);
pref.setSelected(false);
}
}

View File

@@ -29,6 +29,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.widget.TickButtonPreference;
/** A fragment to include each kind of regional preferences. */
public class RegionalPreferencesFragment extends SettingsPreferenceFragment {
@@ -63,7 +64,7 @@ public class RegionalPreferencesFragment extends SettingsPreferenceFragment {
TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(i);
Log.i(TAG, "[onPreferenceClick] key is " + pref.getKey());
if (pref.getKey().equals(preference.getKey())) {
pref.setTickEnable(true);
pref.setSelected(true);
RegionalPreferencesDataUtils.savePreference(
getPrefContext(),
mType,
@@ -72,7 +73,7 @@ public class RegionalPreferencesFragment extends SettingsPreferenceFragment {
? null : preference.getKey());
continue;
}
pref.setTickEnable(false);
pref.setSelected(false);
}
return true;
}
@@ -115,7 +116,7 @@ public class RegionalPreferencesFragment extends SettingsPreferenceFragment {
String value = RegionalPreferencesDataUtils.getDefaultUnicodeExtensionData(
getPrefContext(), mType);
pref.setKey(item);
pref.setTickEnable(!value.isEmpty() && item.equals(value));
pref.setSelected(!value.isEmpty() && item.equals(value));
mPreferenceScreen.addPreference(pref);
}
return super.onCreateView(inflater, container, savedInstanceState);

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.regionalpreferences;
import android.content.Context;
import android.view.View;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.widget.TwoTargetPreference;
/** A preference with tick button */
public class TickButtonPreference extends TwoTargetPreference {
private static final String TAG = TickButtonPreference.class.getSimpleName();
private boolean mIsTickEnabled;
private View mWidgetFrame;
private View mDivider;
public TickButtonPreference(Context context) {
super(context, null);
}
/** Set this preference to be selected. */
public void setTickEnable(boolean isEnable) {
mIsTickEnabled = isEnable;
if (mWidgetFrame != null) {
mWidgetFrame.setVisibility(isEnable ? View.VISIBLE : View.INVISIBLE);
}
}
/** Check if this preference is selected. */
public boolean isTickEnabled() {
return mIsTickEnabled;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mDivider = holder.findViewById(R.id.two_target_divider);
mWidgetFrame = holder.findViewById(android.R.id.widget_frame);
if (mDivider != null) {
mDivider.setVisibility(View.GONE);
}
if (mWidgetFrame != null) {
mWidgetFrame.setVisibility(mIsTickEnabled ? View.VISIBLE : View.INVISIBLE);
}
}
@Override
protected int getSecondTargetResId() {
super.getSecondTargetResId();
return R.layout.preference_widget_tick;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.settings.inputmethod;
package com.android.settings.widget;
import android.content.Context;
import android.view.View;
@@ -25,29 +25,33 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
public class KeyboardLayoutPreference extends Preference {
/** A preference with tick icon. */
public class TickButtonPreference extends Preference {
private ImageView mCheckIcon;
private boolean mIsMark;
private boolean mIsSelected = false;
public KeyboardLayoutPreference(Context context, String layoutName, boolean defaultMark) {
public TickButtonPreference(Context context) {
super(context);
setWidgetLayoutResource(R.layout.preference_check_icon);
setTitle(layoutName);
mIsMark = defaultMark;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mCheckIcon = (ImageView) holder.findViewById(R.id.keyboard_check_icon);
setCheckMark(mIsMark);
mCheckIcon = (ImageView) holder.findViewById(R.id.check_icon);
setSelected(mIsSelected);
}
public void setCheckMark(boolean isMark) {
/** Set icon state.*/
public void setSelected(boolean isSelected) {
if (mCheckIcon != null) {
mCheckIcon.setVisibility(isMark ? View.VISIBLE : View.INVISIBLE);
mIsMark = isMark;
mCheckIcon.setVisibility(isSelected ? View.VISIBLE : View.INVISIBLE);
}
mIsSelected = isSelected;
}
/** Return state of presenting icon. */
public boolean isSelected() {
return mIsSelected;
}
}

View File

@@ -28,7 +28,6 @@ import android.os.UserManager;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentTransaction;
@@ -100,7 +99,6 @@ public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
if (savedInstanceState != null) {
String qrCode = savedInstanceState.getString(KEY_QR_CODE);

View File

@@ -31,13 +31,14 @@ import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.res.Resources;
import android.net.NetworkPolicyManager;
import android.os.UserManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowDashboardFragment;
import com.android.settings.testutils.shadow.ShadowDataUsageUtils;
import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -45,13 +46,15 @@ import com.android.settings.testutils.shadow.ShadowUtils;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
@@ -66,16 +69,22 @@ import org.robolectric.shadows.ShadowTelephonyManager;
})
@RunWith(RobolectricTestRunner.class)
public class DataUsageSummaryTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private UserManager mUserManager;
@Mock
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private NetworkStatsManager mNetworkStatsManager;
private TelephonyManager mTelephonyManager;
private Context mContext;
private Resources mResources;
private FragmentActivity mActivity;
private DataUsageSummary mDataUsageSummary;
/**
* This set up is contrived to get a passing test so that the build doesn't block without tests.
* These tests should be updated as code gets refactored to improve testability.
@@ -83,12 +92,13 @@ public class DataUsageSummaryTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(false).when(mUserManager).isGuestUser();
ShadowApplication shadowContext = ShadowApplication.getInstance();
ShadowUserManager.getShadow().setIsAdminUser(true);
shadowContext.setSystemService(Context.NETWORK_POLICY_SERVICE, mNetworkPolicyManager);
mContext = spy(RuntimeEnvironment.application);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
final ShadowTelephonyManager shadowTelephonyManager = Shadows.shadowOf(mTelephonyManager);
shadowTelephonyManager.setTelephonyManagerForSubscriptionId(
@@ -101,6 +111,12 @@ public class DataUsageSummaryTest {
doReturn(true).when(mResources).getBoolean(R.bool.config_show_sim_info);
doReturn(mNetworkStatsManager).when(mActivity).getSystemService(NetworkStatsManager.class);
mDataUsageSummary = spy(new DataUsageSummary());
doReturn(mContext).when(mDataUsageSummary).getContext();
doNothing().when(mDataUsageSummary).enableProxySubscriptionManager(any());
doReturn(true).when(mDataUsageSummary).removePreference(anyString());
doNothing().when(mDataUsageSummary).addWifiSection();
}
@Test
@@ -201,4 +217,22 @@ public class DataUsageSummaryTest {
verify(dataUsageSummary).addWifiSection();
verify(dataUsageSummary, never()).addMobileSection(anyInt());
}
@Test
public void onCreate_isNotGuestUser_shouldNotFinish() {
doReturn(false).when(mUserManager).isGuestUser();
mDataUsageSummary.onCreate(null);
verify(mDataUsageSummary, never()).finish();
}
@Test
public void onCreate_isGuestUser_shouldFinish() {
doReturn(true).when(mUserManager).isGuestUser();
mDataUsageSummary.onCreate(null);
verify(mDataUsageSummary).finish();
}
}

View File

@@ -26,7 +26,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.PowerManager;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import androidx.preference.PreferenceScreen;
@@ -72,24 +71,6 @@ public class BatterySaverButtonPreferenceControllerTest {
.isEqualTo(SettingsSlicesContract.AUTHORITY);
}
@Test
public void onSwitchChanged_isCheckedButNotAcked_preferenceIsUnchecked() {
setLowPowerWarningAcked(/* acked= */ 0);
mController.onSwitchChanged(/* switchView= */ null, /* isChecked= */ true);
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onSwitchChanged_isCheckedAndAcked_setPowerSaveMode() {
setLowPowerWarningAcked(/* acked= */ 1);
mController.onSwitchChanged(/* switchView= */ null, /* isChecked= */ true);
verify(mPowerManager).setPowerSaveModeEnabled(true);
}
@Test
public void updateState_lowPowerOn_preferenceIsChecked() {
when(mPowerManager.isPowerSaveMode()).thenReturn(true);
@@ -140,16 +121,4 @@ public class BatterySaverButtonPreferenceControllerTest {
public void isPublicSlice_returnsTrue() {
assertThat(mController.isPublicSlice()).isTrue();
}
// 0 means not acked, 1 means acked.
private void setLowPowerWarningAcked(int acked) {
Settings.Secure.putInt(
mContext.getContentResolver(),
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
acked);
Settings.Secure.putInt(
mContext.getContentResolver(),
Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED,
acked);
}
}

View File

@@ -31,6 +31,7 @@ import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
import com.android.settings.testutils.BatteryTestUtils;
@@ -353,6 +354,28 @@ public final class BatteryUsageContentProviderTest {
assertThat(entities.get(0).taskRootPackageName).isEqualTo("com.android.settings2");
}
@Test
public void insert_batteryEvent_returnsExpectedResult() {
mProvider.onCreate();
ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, 10001L);
values.put(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE,
BatteryEventType.POWER_CONNECTED.getNumber());
values.put(BatteryEventEntity.KEY_BATTERY_LEVEL, 66);
final Uri uri = mProvider.insert(DatabaseUtils.BATTERY_EVENT_URI, values);
assertThat(uri).isEqualTo(DatabaseUtils.BATTERY_EVENT_URI);
// Verifies the AppUsageEventEntity content.
final List<BatteryEventEntity> entities =
BatteryStateDatabase.getInstance(mContext).batteryEventDao().getAll();
assertThat(entities).hasSize(1);
assertThat(entities.get(0).timestamp).isEqualTo(10001L);
assertThat(entities.get(0).batteryEventType).isEqualTo(
BatteryEventType.POWER_CONNECTED.getNumber());
assertThat(entities.get(0).batteryLevel).isEqualTo(66);
}
@Test
public void delete_throwsUnsupportedOperationException() {
assertThrows(

View File

@@ -39,6 +39,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import org.junit.Before;
import org.junit.Test;
@@ -194,6 +195,22 @@ public final class ConvertUtilsTest {
.isEqualTo("com.android.settings2");
}
@Test
public void convertBatteryEventToContentValues_normalCase_returnsExpectedContentValues() {
final BatteryEvent batteryEvent =
BatteryEvent.newBuilder()
.setTimestamp(10001L)
.setType(BatteryEventType.POWER_CONNECTED)
.setBatteryLevel(66)
.build();
final ContentValues values =
ConvertUtils.convertBatteryEventToContentValues(batteryEvent);
assertThat(values.getAsLong(BatteryEventEntity.KEY_TIMESTAMP)).isEqualTo(10001L);
assertThat(values.getAsInteger(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)).isEqualTo(
BatteryEventType.POWER_CONNECTED.getNumber());
assertThat(values.getAsInteger(BatteryEventEntity.KEY_BATTERY_LEVEL)).isEqualTo(66);
}
@Test
public void convertToBatteryHistEntry_returnsExpectedResult() {
final int expectedType = 3;
@@ -405,6 +422,15 @@ public final class ConvertUtilsTest {
assertThat(appUsageEvent.getInstanceId()).isEqualTo(0);
}
@Test
public void convertToBatteryEvent_normalCase_returnsExpectedResult() {
final BatteryEvent batteryEvent = ConvertUtils.convertToBatteryEvent(
666L, BatteryEventType.POWER_DISCONNECTED, 88);
assertThat(batteryEvent.getTimestamp()).isEqualTo(666L);
assertThat(batteryEvent.getType()).isEqualTo(BatteryEventType.POWER_DISCONNECTED);
assertThat(batteryEvent.getBatteryLevel()).isEqualTo(88);
}
@Test
public void getLocale_nullContext_returnDefaultLocale() {
assertThat(ConvertUtils.getLocale(/*context=*/ null))

View File

@@ -183,7 +183,7 @@ public final class DataProcessManagerTest {
assertThat(dataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
assertThat(dataProcessManager.getShowScreenOnTime()).isTrue();
final List<AppUsageEvent> appUsageEventList = dataProcessManager.getAppUsageEventList();
Collections.sort(appUsageEventList, DataProcessor.TIMESTAMP_COMPARATOR);
Collections.sort(appUsageEventList, DataProcessor.APP_USAGE_EVENT_TIMESTAMP_COMPARATOR);
assertThat(appUsageEventList.size()).isEqualTo(6);
assertAppUsageEvent(
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);

View File

@@ -250,7 +250,7 @@ public final class DataProcessorTest {
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
DataProcessor.generateAppUsagePeriodMap(
14400000L, hourlyBatteryLevelsPerDay, appUsageEventList);
14400000L, hourlyBatteryLevelsPerDay, appUsageEventList, new ArrayList<>());
assertThat(periodMap).hasSize(3);
// Day 1
@@ -288,7 +288,7 @@ public final class DataProcessorTest {
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
assertThat(DataProcessor.generateAppUsagePeriodMap(
0L, hourlyBatteryLevelsPerDay, new ArrayList<>())).isNull();
0L, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>())).isNull();
}
@Test
@@ -1669,7 +1669,8 @@ public final class DataProcessorTest {
/*instanceId=*/ 4, packageName2));
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
DataProcessor.buildAppUsagePeriodList(appUsageEvents, 0, 5);
DataProcessor.buildAppUsagePeriodList(
appUsageEvents, new ArrayList<>(), 0, 5);
assertThat(appUsagePeriodMap).hasSize(2);
final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
@@ -1693,7 +1694,7 @@ public final class DataProcessorTest {
@Test
public void buildAppUsagePeriodList_emptyEventList_returnNull() {
assertThat(DataProcessor.buildAppUsagePeriodList(
new ArrayList<>(), 0, 1)).isNull();
new ArrayList<>(), new ArrayList<>(), 0, 1)).isNull();
}
@Test
@@ -1705,7 +1706,7 @@ public final class DataProcessorTest {
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 2));
assertThat(DataProcessor.buildAppUsagePeriodList(
appUsageEvents, 0, 3)).isNull();
appUsageEvents, new ArrayList<>(), 0, 3)).isNull();
}
@Test
@@ -1765,6 +1766,89 @@ public final class DataProcessorTest {
assertAppUsagePeriod(appUsagePeriodList.get(6), 1000000, 1100000);
}
@Test
public void excludePowerConnectedTime_startEndNotCharging_returnExpectedResult() {
final List<AppUsagePeriod> appUsagePeriodList = List.of(
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
final List<BatteryEvent> batteryEventList = List.of(
BatteryEvent.newBuilder().setTimestamp(50).setType(
BatteryEventType.POWER_DISCONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(166).setType(
BatteryEventType.POWER_CONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(188).setType(
BatteryEventType.POWER_DISCONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(280).setType(
BatteryEventType.POWER_CONNECTED).build());
final List<AppUsagePeriod> resultList =
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
appUsagePeriodList, batteryEventList);
assertThat(resultList).hasSize(2);
assertAppUsagePeriod(resultList.get(0), 100, 166);
assertAppUsagePeriod(resultList.get(1), 188, 200);
}
@Test
public void excludePowerConnectedTime_startEndInCharging_returnExpectedResult() {
final List<AppUsagePeriod> appUsagePeriodList = List.of(
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
final List<BatteryEvent> batteryEventList = List.of(
BatteryEvent.newBuilder().setTimestamp(50).setType(
BatteryEventType.POWER_DISCONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(80).setType(
BatteryEventType.POWER_CONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(120).setType(
BatteryEventType.POWER_DISCONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(150).setType(
BatteryEventType.POWER_CONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(160).setType(
BatteryEventType.POWER_DISCONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(180).setType(
BatteryEventType.POWER_CONNECTED).build());
final List<AppUsagePeriod> resultList =
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
appUsagePeriodList, batteryEventList);
assertThat(resultList).hasSize(2);
assertAppUsagePeriod(resultList.get(0), 120, 150);
assertAppUsagePeriod(resultList.get(1), 160, 180);
}
@Test
public void excludePowerConnectedTime_wholePeriodNotCharging_returnExpectedResult() {
final List<AppUsagePeriod> appUsagePeriodList = List.of(
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
final List<BatteryEvent> batteryEventList = List.of(
BatteryEvent.newBuilder().setTimestamp(50).setType(
BatteryEventType.POWER_DISCONNECTED).build(),
BatteryEvent.newBuilder().setTimestamp(80).setType(
BatteryEventType.POWER_CONNECTED).build());
final List<AppUsagePeriod> resultList =
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
appUsagePeriodList, batteryEventList);
assertThat(resultList).isEmpty();
}
@Test
public void excludePowerConnectedTime_wholePeriodInCharging_returnExpectedResult() {
final List<AppUsagePeriod> appUsagePeriodList = List.of(
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
final List<BatteryEvent> batteryEventList = List.of(
BatteryEvent.newBuilder().setTimestamp(50).setType(
BatteryEventType.POWER_DISCONNECTED).build());
final List<AppUsagePeriod> resultList =
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
appUsagePeriodList, batteryEventList);
assertThat(resultList).hasSize(1);
assertAppUsagePeriod(resultList.get(0), 100, 200);
}
@Test
public void getScreenOnTime_returnExpectedResult() {
final long userId = 1;

View File

@@ -36,6 +36,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.testutils.BatteryTestUtils;
import org.junit.Before;
@@ -136,6 +137,28 @@ public final class DatabaseUtilsTest {
verifyNoMoreInteractions(mMockContentResolver);
}
@Test
public void sendBatteryEventData_returnsExpectedList() {
final BatteryEvent batteryEvent =
BatteryEvent.newBuilder()
.setTimestamp(10001L)
.setType(BatteryEventType.POWER_CONNECTED)
.setBatteryLevel(66)
.build();
final ContentValues contentValues =
DatabaseUtils.sendBatteryEventData(mContext, batteryEvent);
assertThat(contentValues.getAsInteger(BatteryEventEntity.KEY_TIMESTAMP))
.isEqualTo(10001L);
assertThat(contentValues.getAsInteger(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE))
.isEqualTo(BatteryEventType.POWER_CONNECTED.getNumber());
assertThat(contentValues.getAsInteger(BatteryEventEntity.KEY_BATTERY_LEVEL))
.isEqualTo(66);
// Verifies the inserted ContentValues into content provider.
verify(mMockContentResolver).insert(DatabaseUtils.BATTERY_EVENT_URI, contentValues);
}
@Test
public void sendBatteryEntryData_nullBatteryIntent_returnsNullValue() {
doReturn(null).when(mContext).registerReceiver(any(), any());

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.database.Cursor;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.BatteryTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Tests for {@link BatteryEventDao}. */
@RunWith(RobolectricTestRunner.class)
public final class BatteryEventDaoTest {
private Context mContext;
private BatteryStateDatabase mDatabase;
private BatteryEventDao mBatteryEventDao;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
mBatteryEventDao = mDatabase.batteryEventDao();
}
@After
public void closeDb() {
mDatabase.close();
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
}
@Test
public void getAllAfter_returnExpectedResult() {
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(100L)
.setBatteryEventType(1)
.setBatteryLevel(66)
.build());
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(200L)
.setBatteryEventType(2)
.setBatteryLevel(88)
.build());
final Cursor cursor = mBatteryEventDao.getAllAfter(160L);
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
assertThat(cursor.getLong(cursor.getColumnIndex(BatteryEventEntity.KEY_TIMESTAMP)))
.isEqualTo(200L);
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)))
.isEqualTo(2);
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_LEVEL)))
.isEqualTo(88);
mBatteryEventDao.clearAll();
assertThat(mBatteryEventDao.getAll()).isEmpty();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Tests for {@link BatteryEventEntity}. */
@RunWith(RobolectricTestRunner.class)
public final class BatteryEventEntityTest {
@Test
public void testBuilder_returnsExpectedResult() {
final long timestamp = 10001L;
final int batteryEventType = 1;
final int batteryLevel = 66;
BatteryEventEntity entity = BatteryEventEntity
.newBuilder()
.setTimestamp(timestamp)
.setBatteryEventType(batteryEventType)
.setBatteryLevel(batteryLevel)
.build();
// Verifies the app relative information.
assertThat(entity.timestamp).isEqualTo(timestamp);
assertThat(entity.batteryEventType).isEqualTo(batteryEventType);
assertThat(entity.batteryLevel).isEqualTo(batteryLevel);
}
}

View File

@@ -16,10 +16,6 @@
package com.android.settings.wifi.dpp;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -76,12 +72,6 @@ public class WifiDppConfiguratorActivityTest {
Robolectric.setupActivity(WifiDppConfiguratorActivity.class);
}
@Test
public void launchActivity_shouldAddFlagSecure() {
assertThat(mActivity.getWindow().getAttributes().flags & FLAG_SECURE)
.isEqualTo(FLAG_SECURE);
}
@Test
public void handleIntent_isGuestUser_shouldFinish() {
when(mUserManager.isGuestUser()).thenReturn(true);

View File

@@ -33,6 +33,8 @@ import androidx.preference.PreferenceScreen;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.widget.TickButtonPreference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;