Skip to content

Commit

Permalink
Settings: Make google's battery widget work
Browse files Browse the repository at this point in the history
Change-Id: I763a3bdb7bc0385fce04fe2635f06e601f9557ab
Signed-off-by: Jis G Jacob <[email protected]>
  • Loading branch information
ReallySnow authored and StudioKeys committed Jun 16, 2024
1 parent 5f5c963 commit 8dd0e70
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 0 deletions.
16 changes: 16 additions & 0 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5134,6 +5134,22 @@
android:permission="android.permission.NETWORK_SETTINGS">
</activity>

<!-- Phone and bluetooth device battery data -->
<service
android:name="com.android.settings.fuelgauge.batterydata.BatteryDataFetchService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"
android:process=":usage_data_loader"/>

<receiver
android:name="com.android.settings.fuelgauge.batterydata.BatteryDataBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="settings.intelligence.battery.action.FETCH_BATTERY_USAGE_DATA"/>
<action android:name="settings.intelligence.battery.action.FETCH_BLUETOOTH_BATTERY_DATA"/>
</intent-filter>
</receiver>

<!-- This is the longest AndroidManifest.xml ever. -->
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Copyright (C) 2022 The Project Mia
//
// SPDX-License-Identifier: Apache-2.0
//

package com.android.settings.fuelgauge.batterydata;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public final class BatteryDataBroadcastReceiver extends BroadcastReceiver {

private static final String TAG = "BatteryDataBroadcastReceiver";

public boolean mFetchBatteryUsageData;

@Override
public void onReceive(Context context, Intent intent) {
String batteryData = intent.getAction();
switch (batteryData) {
// Fetch device usage data
case "settings.intelligence.battery.action.FETCH_BATTERY_USAGE_DATA":
mFetchBatteryUsageData = true;
BatteryDataFetchService.enqueueWork(context);
break;
// Fetch bluetooth device usage data
case "settings.intelligence.battery.action.FETCH_BLUETOOTH_BATTERY_DATA":
try {
BluetoothBatteryDataFetch.returnBluetoothDevices(context, intent);
} catch (Exception e) {
Log.e(TAG, "returnBluetoothDevices() error: ", e);
}
break;
default:
break;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright (C) 2022 The Project Mia
//
// SPDX-License-Identifier: Apache-2.0
//

package com.android.settings.fuelgauge.batterydata;

import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.core.app.JobIntentService;

import java.util.List;

public class BatteryDataFetchService extends JobIntentService {

private static final String TAG = "BatteryDataFetchService";
private static final Intent JOB_INTENT = new Intent("action.LOAD_BATTERY_USAGE_DATA");

public static void enqueueWork(final Context context) {
AsyncTask.execute(() -> {
loadUsageDataSafely(context);
});
}

@Override
protected void onHandleWork(@NonNull Intent intent) {
loadUsageDataSafely(this);
}

private static void loadUsageDataSafely(Context context) {
try {
loadUsageData(context);
} catch (RuntimeException e) {
Log.e(TAG, "Fail load usage data:" + e);
}
}

private static void loadUsageData(Context context) {
BatteryUsageStats batteryUsageStats = context
.getSystemService(BatteryStatsManager.class)
.getBatteryUsageStats(new BatteryUsageStatsQuery.Builder()
.includeBatteryHistory()
.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//
// Copyright (C) 2022 The Project Mia
//
// SPDX-License-Identifier: Apache-2.0
//

package com.android.settings.fuelgauge.batterydata;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ResultReceiver;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public final class BluetoothBatteryDataFetch {

private static final String TAG = "BluetoothBatteryDataFetch";

@VisibleForTesting
public static LocalBluetoothManager mLocalBluetoothManager;
private static Context context;
private static Intent intent;

private static String emptyIfNull(String value) {
return value == null ? "" : value;
}

public static ContentValues wrapBluetoothData(
Context context, CachedBluetoothDevice cachedBluetoothDevice,
boolean nonbuds) {
BluetoothDevice device = cachedBluetoothDevice.getDevice();

ContentValues contentValues = new ContentValues();
contentValues.put("type", device.getType());
contentValues.put("name", emptyIfNull(device.getName()));
contentValues.put("alias", emptyIfNull(device.getAlias()));
contentValues.put("address", emptyIfNull(device.getAddress()));
contentValues.put("batteryLevel", device.getBatteryLevel());

putStringMetadata(contentValues, "hardwareVersion", device.getMetadata(
BluetoothDevice.METADATA_HARDWARE_VERSION));
putStringMetadata(contentValues, "batteryLevelRight", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY));
putStringMetadata(contentValues, "batteryLevelLeft", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY));
putStringMetadata(contentValues, "batteryLevelCase", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY));
putStringMetadata(contentValues, "batteryChargingRight", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING));
putStringMetadata(contentValues, "batteryChargingLeft", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING));
putStringMetadata(contentValues, "batteryChargingCase", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING));
putStringMetadata(contentValues, "batteryChargingMain", device.getMetadata(
BluetoothDevice.METADATA_MAIN_CHARGING));
if (nonbuds) {
putStringMetadata(contentValues, "deviceIconMain", device.getMetadata(
BluetoothDevice.METADATA_MAIN_ICON));
putStringMetadata(contentValues, "deviceIconCase", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_CASE_ICON));
putStringMetadata(contentValues, "deviceIconLeft", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON));
putStringMetadata(contentValues, "deviceIconRight", device.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON));
}
BluetoothClass bluetoothClass = device.getBluetoothClass();
if (bluetoothClass != null) {
contentValues.put("bluetoothClass", marshall(bluetoothClass));
}
return contentValues;
}

private static byte[] marshall(Parcelable parcelable) {
Parcel obtain = Parcel.obtain();
parcelable.writeToParcel(obtain, 0);
byte[] marshall = obtain.marshall();
obtain.recycle();
return marshall;
}

private static void putStringMetadata(
ContentValues contentValues, String key, byte[] value) {
if (value == null || value.length == 0) {
return;
}
contentValues.put(key, new String(value));
}

public static void returnBluetoothDevices(Context context, Intent intent) {
AsyncTask.execute(() -> returnBluetoothDevicesInner(context, intent));
}

public static void returnBluetoothDevicesInner(Context context, Intent intent) {
ResultReceiver resultReceiver = intent.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER);
if (resultReceiver == null) {
Log.w(TAG, "No result receiver found from intent");
return;
}
if (mLocalBluetoothManager == null) {
mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, null);
}
BluetoothAdapter adapter = context.getSystemService(BluetoothManager.class).getAdapter();
if (adapter == null || !adapter.isEnabled() || mLocalBluetoothManager == null) {
Log.w(TAG, "BluetoothAdapter not present or not enabled");
resultReceiver.send(1, null);
return;
}
sendAndFilterBluetoothData(context, resultReceiver, mLocalBluetoothManager,
intent.getBooleanExtra("extra_fetch_icon", false));
}

public static void sendAndFilterBluetoothData(Context context,
ResultReceiver resultReceiver,
LocalBluetoothManager localBluetoothManager,
boolean cache) {
long start = System.currentTimeMillis();
Collection<CachedBluetoothDevice> cachedDevicesCopy =
localBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy();
Log.d(TAG, "cachedDevices:" + cachedDevicesCopy);
if (cachedDevicesCopy == null || cachedDevicesCopy.isEmpty()) {
resultReceiver.send(0, Bundle.EMPTY);
return;
}
List<CachedBluetoothDevice> connectedDevices = cachedDevicesCopy.stream()
.filter(CachedBluetoothDevice::isConnected)
.collect(Collectors.toList());
Log.d(TAG, "Connected devices:" + connectedDevices);
if (connectedDevices.isEmpty()) {
resultReceiver.send(0, Bundle.EMPTY);
return;
}
ArrayList<ContentValues> bluetoothWrapDataListKey = new ArrayList<>();
ArrayList<BluetoothDevice> bluetoothParcelableList = new ArrayList<>();
connectedDevices.forEach(cachedBluetoothDevice -> {
BluetoothDevice device = cachedBluetoothDevice.getDevice();
bluetoothParcelableList.add(device);
try {
bluetoothWrapDataListKey.add(
wrapBluetoothData(context, cachedBluetoothDevice, cache));
} catch (Exception e) {
Log.e(TAG, "Wrap bluetooth data failed: " + device, e);
}
});
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("bluetoothParcelableListKey", bluetoothParcelableList);
if (!bluetoothWrapDataListKey.isEmpty()) {
bundle.putParcelableArrayList("bluetoothWrapDataListKey", bluetoothWrapDataListKey);
}
resultReceiver.send(0, bundle);
Log.d(TAG, String.format("Send and filter bluetooth data size=%d in %d/ms",
bluetoothWrapDataListKey.size(), (System.currentTimeMillis() - start)));
}
}

0 comments on commit 8dd0e70

Please sign in to comment.