Loading AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -3031,6 +3031,11 @@ android:authorities="${applicationId}.battery.usage.provider" android:permission="com.android.settings.BATTERY_DATA"/> <provider android:name=".fuelgauge.batteryusage.bugreport.BugReportContentProvider" android:exported="false" android:authorities="${applicationId}.battery.usage.bugreport"/> <receiver android:name=".fuelgauge.batteryusage.BatteryUsageBroadcastReceiver" android:exported="true"> <intent-filter> Loading src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * 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.bugreport; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.util.Log; import com.android.settings.fuelgauge.batteryusage.DatabaseUtils; import java.io.FileDescriptor; import java.io.PrintWriter; /** Provides debug information in the bugreports when device is dumpping. */ public final class BugReportContentProvider extends ContentProvider { private static final String TAG = "BugReportContentProvider"; @Override public boolean onCreate() { return true; } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final Context context = getContext(); if (context == null) { Log.w(TAG, "failed to dump BatteryUsage state: null context"); return; } if (DatabaseUtils.isWorkProfile(context)) { Log.w(TAG, "ignore battery usage states dump in the work profile"); return; } writer.println("dump BatteryUsage states:"); LogUtils.dumpUsageDatabaseHist(context, writer); } @Override public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { throw new UnsupportedOperationException("unsupported!"); } @Override public String getType(Uri uri) { throw new UnsupportedOperationException("unsupported!"); } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException("unsupported!"); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("unsupported!"); } @Override public int update( Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("unsupported!"); } } src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * 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.bugreport; import android.content.Context; import android.util.Log; import com.android.settings.fuelgauge.batteryusage.db.BatteryState; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.time.Clock; import java.time.Duration; import java.util.Date; import java.util.List; import java.util.Locale; /** A utility class to aggregate and provide required log data. */ public final class LogUtils { private static final String TAG = "LogUtils"; private static final Duration DUMP_TIME_OFFSET = Duration.ofHours(24); private static final Duration DUMP_TIME_OFFSET_FOR_ENTRY = Duration.ofHours(4); @SuppressWarnings("JavaUtilDate") static void dumpUsageDatabaseHist(Context context, PrintWriter writer) { final BatteryStateDao dao = BatteryStateDatabase .getInstance(context.getApplicationContext()) .batteryStateDao(); final long timeOffset = Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis(); // Gets all distinct timestamps. final List<Long> timestamps = dao.getDistinctTimestamps(timeOffset); final int distinctCount = timestamps.size(); writer.println("\n\tDatabaseHistory:"); writer.println("distinct timestamp count:" + distinctCount); Log.w(TAG, "distinct timestamp count:" + distinctCount); if (distinctCount == 0) { return; } // Dumps all distinct timestamps. final SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, HH:mm:ss", Locale.US); timestamps.forEach(timestamp -> { final String formattedTimestamp = formatter.format(new Date(timestamp)); writer.println("\t" + formattedTimestamp); Log.w(TAG, "\t" + formattedTimestamp); }); final List<BatteryState> stateList = dao.getAllAfter( Clock.systemUTC().millis() - DUMP_TIME_OFFSET_FOR_ENTRY.toMillis()); stateList.stream().forEach(state -> writer.println(state)); } private LogUtils() {} } tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProviderTest.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * 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.bugreport; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import androidx.test.core.app.ApplicationProvider; import com.android.settings.testutils.BatteryTestUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; /** Tests of {@link BugReportContentProvider}. */ @RunWith(RobolectricTestRunner.class) public final class BugReportContentProviderTest { private static final String PACKAGE_NAME1 = "com.android.settings"; private static final String PACKAGE_NAME2 = "com.android.systemui"; private Context mContext; private PrintWriter mPrintWriter; private StringWriter mStringWriter; private BugReportContentProvider mBugReportContentProvider; @Before public void setUp() { mStringWriter = new StringWriter(); mPrintWriter = new PrintWriter(mStringWriter); mContext = ApplicationProvider.getApplicationContext(); mBugReportContentProvider = new BugReportContentProvider(); mBugReportContentProvider.attachInfo(mContext, /*info=*/ null); // Inserts fake data into database for testing. BatteryTestUtils.setUpBatteryStateDatabase(mContext); BatteryTestUtils.insertDataToBatteryStateDatabase( mContext, System.currentTimeMillis(), PACKAGE_NAME1); BatteryTestUtils.insertDataToBatteryStateDatabase( mContext, System.currentTimeMillis(), PACKAGE_NAME2); } @Test public void dump_nullContext_notDumpsBatteryUsageData() { mBugReportContentProvider = new BugReportContentProvider(); mBugReportContentProvider.attachInfo(/*context=*/ null, /*info=*/ null); mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {}); assertThat(mStringWriter.toString()).isEmpty(); } @Test public void dump_inWorkProfileMode_notDumpsBatteryUsageData() { BatteryTestUtils.setWorkProfile(mContext); mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {}); assertThat(mStringWriter.toString()).isEmpty(); } @Test public void dump_dumpsBatteryUsageHistory() { mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {}); String dumpContent = mStringWriter.toString(); assertThat(dumpContent).contains("DatabaseHistory"); assertThat(dumpContent).contains(PACKAGE_NAME1); assertThat(dumpContent).contains(PACKAGE_NAME2); assertThat(dumpContent).contains("distinct timestamp count:2"); } } Loading
AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -3031,6 +3031,11 @@ android:authorities="${applicationId}.battery.usage.provider" android:permission="com.android.settings.BATTERY_DATA"/> <provider android:name=".fuelgauge.batteryusage.bugreport.BugReportContentProvider" android:exported="false" android:authorities="${applicationId}.battery.usage.bugreport"/> <receiver android:name=".fuelgauge.batteryusage.BatteryUsageBroadcastReceiver" android:exported="true"> <intent-filter> Loading
src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * 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.bugreport; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.util.Log; import com.android.settings.fuelgauge.batteryusage.DatabaseUtils; import java.io.FileDescriptor; import java.io.PrintWriter; /** Provides debug information in the bugreports when device is dumpping. */ public final class BugReportContentProvider extends ContentProvider { private static final String TAG = "BugReportContentProvider"; @Override public boolean onCreate() { return true; } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final Context context = getContext(); if (context == null) { Log.w(TAG, "failed to dump BatteryUsage state: null context"); return; } if (DatabaseUtils.isWorkProfile(context)) { Log.w(TAG, "ignore battery usage states dump in the work profile"); return; } writer.println("dump BatteryUsage states:"); LogUtils.dumpUsageDatabaseHist(context, writer); } @Override public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { throw new UnsupportedOperationException("unsupported!"); } @Override public String getType(Uri uri) { throw new UnsupportedOperationException("unsupported!"); } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException("unsupported!"); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("unsupported!"); } @Override public int update( Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("unsupported!"); } }
src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * 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.bugreport; import android.content.Context; import android.util.Log; import com.android.settings.fuelgauge.batteryusage.db.BatteryState; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.time.Clock; import java.time.Duration; import java.util.Date; import java.util.List; import java.util.Locale; /** A utility class to aggregate and provide required log data. */ public final class LogUtils { private static final String TAG = "LogUtils"; private static final Duration DUMP_TIME_OFFSET = Duration.ofHours(24); private static final Duration DUMP_TIME_OFFSET_FOR_ENTRY = Duration.ofHours(4); @SuppressWarnings("JavaUtilDate") static void dumpUsageDatabaseHist(Context context, PrintWriter writer) { final BatteryStateDao dao = BatteryStateDatabase .getInstance(context.getApplicationContext()) .batteryStateDao(); final long timeOffset = Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis(); // Gets all distinct timestamps. final List<Long> timestamps = dao.getDistinctTimestamps(timeOffset); final int distinctCount = timestamps.size(); writer.println("\n\tDatabaseHistory:"); writer.println("distinct timestamp count:" + distinctCount); Log.w(TAG, "distinct timestamp count:" + distinctCount); if (distinctCount == 0) { return; } // Dumps all distinct timestamps. final SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, HH:mm:ss", Locale.US); timestamps.forEach(timestamp -> { final String formattedTimestamp = formatter.format(new Date(timestamp)); writer.println("\t" + formattedTimestamp); Log.w(TAG, "\t" + formattedTimestamp); }); final List<BatteryState> stateList = dao.getAllAfter( Clock.systemUTC().millis() - DUMP_TIME_OFFSET_FOR_ENTRY.toMillis()); stateList.stream().forEach(state -> writer.println(state)); } private LogUtils() {} }
tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProviderTest.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * 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.bugreport; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import androidx.test.core.app.ApplicationProvider; import com.android.settings.testutils.BatteryTestUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; /** Tests of {@link BugReportContentProvider}. */ @RunWith(RobolectricTestRunner.class) public final class BugReportContentProviderTest { private static final String PACKAGE_NAME1 = "com.android.settings"; private static final String PACKAGE_NAME2 = "com.android.systemui"; private Context mContext; private PrintWriter mPrintWriter; private StringWriter mStringWriter; private BugReportContentProvider mBugReportContentProvider; @Before public void setUp() { mStringWriter = new StringWriter(); mPrintWriter = new PrintWriter(mStringWriter); mContext = ApplicationProvider.getApplicationContext(); mBugReportContentProvider = new BugReportContentProvider(); mBugReportContentProvider.attachInfo(mContext, /*info=*/ null); // Inserts fake data into database for testing. BatteryTestUtils.setUpBatteryStateDatabase(mContext); BatteryTestUtils.insertDataToBatteryStateDatabase( mContext, System.currentTimeMillis(), PACKAGE_NAME1); BatteryTestUtils.insertDataToBatteryStateDatabase( mContext, System.currentTimeMillis(), PACKAGE_NAME2); } @Test public void dump_nullContext_notDumpsBatteryUsageData() { mBugReportContentProvider = new BugReportContentProvider(); mBugReportContentProvider.attachInfo(/*context=*/ null, /*info=*/ null); mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {}); assertThat(mStringWriter.toString()).isEmpty(); } @Test public void dump_inWorkProfileMode_notDumpsBatteryUsageData() { BatteryTestUtils.setWorkProfile(mContext); mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {}); assertThat(mStringWriter.toString()).isEmpty(); } @Test public void dump_dumpsBatteryUsageHistory() { mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {}); String dumpContent = mStringWriter.toString(); assertThat(dumpContent).contains("DatabaseHistory"); assertThat(dumpContent).contains(PACKAGE_NAME1); assertThat(dumpContent).contains(PACKAGE_NAME2); assertThat(dumpContent).contains("distinct timestamp count:2"); } }