From 4ebfedd7f870e181a62daa2ab70b9d9155492bac Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Thu, 17 Jun 2021 13:16:38 -0700 Subject: Change ownership of the account request notification. Add "Permission requested by Application..." string. Test: manual Bug: 179338675 Change-Id: Ib66ccc1b39bd1f3f8fa3b1efc38a9d413b72a321 (cherry picked from commit 26de0c231ffb9fd8d22e80ca120c766c26276779) --- core/res/res/values/strings.xml | 2 ++ core/res/res/values/symbols.xml | 1 + .../server/accounts/AccountManagerService.java | 21 +++++++++++++++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d6ee28b93f92..5c659123b027 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3780,6 +3780,8 @@ Deny Permission requested Permission requested\nfor account %s. + + Permission requested by %1$s\nfor account %2$s. You\'re using this app outside of your work profile diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3ef0a8dc9daa..3c2aa621864b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -548,6 +548,7 @@ + diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 2e04f9c2bcd1..db3c25a7e43a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -449,7 +449,7 @@ public class AccountManagerService if (!checkAccess || hasAccountAccess(account, packageName, UserHandle.getUserHandleForUid(uid))) { cancelNotification(getCredentialPermissionNotificationId(account, - AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), UserHandle.getUserHandleForUid(uid)); } } @@ -3051,8 +3051,8 @@ public class AccountManagerService String authTokenType = intent.getStringExtra( GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); final String titleAndSubtitle = - mContext.getString(R.string.permission_request_notification_with_subtitle, - account.name); + mContext.getString(R.string.permission_request_notification_for_app_with_subtitle, + getApplicationLabel(packageName), account.name); final int index = titleAndSubtitle.indexOf('\n'); String title = titleAndSubtitle; String subtitle = ""; @@ -3075,7 +3075,16 @@ public class AccountManagerService null, user)) .build(); installNotification(getCredentialPermissionNotificationId( - account, authTokenType, uid), n, packageName, user.getIdentifier()); + account, authTokenType, uid), n, "android", user.getIdentifier()); + } + + private String getApplicationLabel(String packageName) { + try { + return mPackageManager.getApplicationLabel( + mPackageManager.getApplicationInfo(packageName, 0)).toString(); + } catch (PackageManager.NameNotFoundException e) { + return packageName; + } } private Intent newGrantCredentialsPermissionIntent(Account account, String packageName, @@ -3111,7 +3120,7 @@ public class AccountManagerService nId = accounts.credentialsPermissionNotificationIds.get(key); if (nId == null) { String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION - + ":" + account.hashCode() + ":" + authTokenType.hashCode(); + + ":" + account.hashCode() + ":" + authTokenType.hashCode() + ":" + uid; int id = SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION; nId = new NotificationId(tag, id); accounts.credentialsPermissionNotificationIds.put(key, nId); @@ -4064,7 +4073,7 @@ public class AccountManagerService private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { cancelNotification(getCredentialPermissionNotificationId(account, - AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), UserHandle.getUserHandleForUid(uid)); if (callback != null) { Bundle result = new Bundle(); -- cgit v1.2.3 From 4eba7e65cd0cc2f2c87b001fb34b9f28ee7c70ab Mon Sep 17 00:00:00 2001 From: Bernardo Rufino Date: Wed, 26 May 2021 16:25:39 +0100 Subject: Fix background bypass via notifications This is a CP of ag/14736230 to qt-dev. Apps were able to bypass BAL and BG-FGS restrictions by retrieving their own notifications and firing their PI since those were allowlisted for those operations. Now we strip the token that granted them that ability from notifications returned via NM.getActiveNotifications(), which returns the notifications of the caller. Notifications returned via notification listener APIs still contain such token, as they should. Bug: 185388103 Bug: 169821287 Test: Manually tested Change-Id: I2ede0d639a560f6acacec3864a0a7d23af152ba5 Merged-In: I2ede0d639a560f6acacec3864a0a7d23af152ba5 (cherry picked from commit 5fbeff59df3ea1441c3843aa1834616876ef1985) (cherry picked from commit 14c1c7b4a732c517ba18f5dd0598adb9f3b72221) --- core/java/android/app/Notification.java | 13 +++++++++++++ .../server/notification/NotificationManagerService.java | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c273cf08d03b..132afabe82d1 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3028,6 +3028,19 @@ public class Notification implements Parcelable builder.build(); // callers expect this notification to be ready to use } + /** + * Sets the token used for background operations for the pending intents associated with this + * notification. + * + * This token is automatically set during deserialization for you, you usually won't need to + * call this unless you want to change the existing token, if any. + * + * @hide + */ + public void setAllowlistToken(@Nullable IBinder token) { + mWhitelistToken = token; + } + /** * @hide */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index dc551e14b970..629a918584fe 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3859,6 +3859,7 @@ public class NotificationManagerService extends SystemService { } } + /** Notifications returned here will have allowlistToken stripped from them. */ private StatusBarNotification sanitizeSbn(String pkg, int userId, StatusBarNotification sbn) { if (sbn.getUserId() == userId) { @@ -3866,11 +3867,16 @@ public class NotificationManagerService extends SystemService { // We could pass back a cloneLight() but clients might get confused and // try to send this thing back to notify() again, which would not work // very well. + Notification notification = sbn.getNotification().clone(); + // Remove background token before returning notification to untrusted app, this + // ensures the app isn't able to perform background operations that are + // associated with notification interactions. + notification.setAllowlistToken(null); return new StatusBarNotification( sbn.getPackageName(), sbn.getOpPkg(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), - sbn.getNotification().clone(), + notification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); } } -- cgit v1.2.3 From 0c4bc631f2e09ccd0712da39b3e1a6c4eaf52a65 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 22 Apr 2021 16:55:09 -0400 Subject: Fix a potential thread safety issue in VectorDrawable Bug: 158839504 Bug: 185178568 Test: speculative Change-Id: Id9f229f08fe5897dda25441fbaa15c98f8130de9 (cherry picked from commit 304f3af54526f3d80cc037e18f4cf89f1053737c) --- graphics/java/android/graphics/drawable/VectorDrawable.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index e6fa866df3ab..a831bb86009c 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -348,15 +348,19 @@ public class VectorDrawable extends Drawable { private final Rect mTmpBounds = new Rect(); public VectorDrawable() { - this(new VectorDrawableState(null), null); + this(null, null); } /** * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) { - mVectorState = state; + private VectorDrawable(@Nullable VectorDrawableState state, @Nullable Resources res) { + // As the mutable, not-thread-safe native instance is stored in VectorDrawableState, we + // need to always do a defensive copy even if mutate() isn't called. Otherwise + // draw() being called on 2 different VectorDrawable instances could still hit the same + // underlying native object. + mVectorState = new VectorDrawableState(state); updateLocalState(res); } -- cgit v1.2.3 From 42c16edc958fa196e934e361205f74727db8285b Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 21 Jul 2021 10:08:04 -0400 Subject: Guard DISABLE_PLUGIN with PLUGIN permission. Fixes a p0 security bug. We already have the plugin permission defined in our manifest. Ensure that senders of the DISABLE_PLUGIN broadcast have that permission. Bug: 193444889 Test: manual Change-Id: Iebaba435c17c5644c5357c0683858447f5ffb897 Merged-In: Iebaba435c17c5644c5357c0683858447f5ffb897 (cherry picked from commit c2c2fa79ffae28d40d0d8bfb29f25b6ede6bd55b) --- .../src/com/android/systemui/shared/plugins/PluginManagerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 4d1fb38329e9..a687bb89fd75 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -195,9 +195,12 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + mContext.registerReceiver(this, filter); filter.addAction(PLUGIN_CHANGED); filter.addAction(DISABLE_PLUGIN); filter.addDataScheme("package"); + mContext.registerReceiver(this, filter, PluginInstanceManager.PLUGIN_PERMISSION, null); mContext.registerReceiver(this, filter); filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiver(this, filter); -- cgit v1.2.3 From bfad9c3e8e1e920b03c06beae0cb0fa77af81126 Mon Sep 17 00:00:00 2001 From: Sudheer Shanka Date: Tue, 11 May 2021 17:01:11 -0700 Subject: Send targeted broadcasts to prevent other apps from receiving them. When sending broadcasts ACTION_SNOOZE_WARNING in NPMS, which may contain sensitive information, explicitly set the package name that should receive it to prevent other apps from receiving them. Bug: 177931370 Test: manual Change-Id: I11d736771d859d2af27d5c84a502ab038974e2e2 Merged-In: I11d736771d859d2af27d5c84a502ab038974e2e2 (cherry picked from commit 1494979a6ac772fa49a26748f047a4c3d64baf51) (cherry picked from commit 16732a1253294866c588ca040416c48be734490b) --- .../com/android/server/net/NetworkPolicyManagerService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 42bd8c512806..4873440b53c1 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -1370,7 +1370,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setSmallIcon(R.drawable.stat_notify_error); - final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template); + final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template, + mContext.getPackageName()); builder.setDeleteIntent(PendingIntent.getBroadcast( mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); @@ -1456,7 +1457,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setSmallIcon(R.drawable.stat_notify_error); - final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template); + final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template, + mContext.getPackageName()); builder.setDeleteIntent(PendingIntent.getBroadcast( mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); @@ -5061,17 +5063,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return new Intent(ACTION_ALLOW_BACKGROUND); } - private static Intent buildSnoozeWarningIntent(NetworkTemplate template) { + private static Intent buildSnoozeWarningIntent(NetworkTemplate template, String targetPackage) { final Intent intent = new Intent(ACTION_SNOOZE_WARNING); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); + intent.setPackage(targetPackage); return intent; } - private static Intent buildSnoozeRapidIntent(NetworkTemplate template) { + private static Intent buildSnoozeRapidIntent(NetworkTemplate template, String targetPackage) { final Intent intent = new Intent(ACTION_SNOOZE_RAPID); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(EXTRA_NETWORK_TEMPLATE, template); + intent.setPackage(targetPackage); return intent; } -- cgit v1.2.3 From efdcec1e29a4179aeb5df314321bb1d5e1ba0bee Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Fri, 30 Jul 2021 15:52:05 +0800 Subject: DO NOT MERGE Apply a maximum char count to the load label api The system is overwhelmed by an enormous label string returned by the load label api. This cl truncates the label string if it exceeds the maximum safe length. Bug: 67013844 Test: atest PackageManagerTest Change-Id: Ia4d768cc93a47cfb8b6f7c4b6dc73abd801809bd Merged-in: Ia4d768cc93a47cfb8b6f7c4b6dc73abd801809bd (cherry picked from commit 3c26a24644feaf2860892809929dec816f354bae) --- core/java/android/content/pm/PackageItemInfo.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 65ce1e7ef079..9cd568fe2aaf 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -207,7 +207,9 @@ public class PackageItemInfo { return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE); } else { - return loadUnsafeLabel(pm); + // Trims the label string to the MAX_SAFE_LABEL_LENGTH. This is to prevent that the + // system is overwhelmed by an enormous string returned by the application. + return TextUtils.trimToSize(loadUnsafeLabel(pm), MAX_SAFE_LABEL_LENGTH); } } -- cgit v1.2.3 From 0974700666a209e3495510eb2f6a2579ea0d8a04 Mon Sep 17 00:00:00 2001 From: lucaslin Date: Thu, 29 Jul 2021 13:26:35 +0800 Subject: Make sure that only the owner can call stopVpnProfile() In stopVpnProfile(), it doesn't check if the caller's package name is the same as the given one, so any app has chance to stop the VPN profile of other apps. Bug: 191382886 Test: atest FrameworksNetTests CtsNetTestCases \ CtsHostsideNetworkTests:HostsideVpnTests Change-Id: Ib0a6e9ed191ff8c8bd55ce9902d894b6a339ace2 Merged-In: I254ffd1c08ec058d594b4ea55cbae5505f8497cc (cherry picked from commit f3072fcd46112bad7c5f6ddd4cc35d2c67f00d11) --- .../com/android/server/ConnectivityService.java | 28 ++++++++++++++++++++-- .../android/server/ConnectivityServiceTest.java | 18 ++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index a1cbd00e360f..9a3ab4403831 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -73,6 +73,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.database.ContentObserver; import android.net.CaptivePortal; @@ -4609,6 +4610,25 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private int getAppUid(final String app, final int userId) { + final PackageManager pm = mContext.getPackageManager(); + final long token = Binder.clearCallingIdentity(); + try { + return pm.getPackageUidAsUser(app, userId); + } catch (NameNotFoundException e) { + return -1; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void verifyCallingUidAndPackage(String packageName, int callingUid) { + final int userId = UserHandle.getUserId(callingUid); + if (getAppUid(packageName, userId) != callingUid) { + throw new SecurityException(packageName + " does not belong to uid " + callingUid); + } + } + /** * Starts the VPN based on the stored profile for the given package * @@ -4620,7 +4640,9 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public void startVpnProfile(@NonNull String packageName) { - final int user = UserHandle.getUserId(Binder.getCallingUid()); + final int callingUid = Binder.getCallingUid(); + verifyCallingUidAndPackage(packageName, callingUid); + final int user = UserHandle.getUserId(callingUid); synchronized (mVpns) { throwIfLockdownEnabled(); mVpns.get(user).startVpnProfile(packageName, mKeyStore); @@ -4637,7 +4659,9 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public void stopVpnProfile(@NonNull String packageName) { - final int user = UserHandle.getUserId(Binder.getCallingUid()); + final int callingUid = Binder.getCallingUid(); + verifyCallingUidAndPackage(packageName, callingUid); + final int user = UserHandle.getUserId(callingUid); synchronized (mVpns) { mVpns.get(user).stopVpnProfile(packageName); } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a5554c740e7f..e9301d1dea9d 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1224,6 +1224,9 @@ public class ConnectivityServiceTest { Arrays.asList(new UserInfo[] { new UserInfo(VPN_USER, "", 0), })); + final int userId = UserHandle.getCallingUserId(); + final UserInfo primaryUser = new UserInfo(userId, "", UserInfo.FLAG_PRIMARY); + doReturn(primaryUser).when(mUserManager).getUserInfo(eq(userId)); final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) @@ -1368,6 +1371,9 @@ public class ConnectivityServiceTest { buildPackageInfo(/* SYSTEM */ false, APP2_UID), buildPackageInfo(/* SYSTEM */ false, VPN_UID) })); + final int userId = UserHandle.getCallingUserId(); + when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId)) + .thenReturn(Process.myUid()); } private void verifyActiveNetwork(int transport) { @@ -7068,6 +7074,18 @@ public class ConnectivityServiceTest { assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); } + @Test + public void testStartVpnProfileFromDiffPackage() throws Exception { + final String notMyVpnPkg = "com.not.my.vpn"; + assertThrows(SecurityException.class, () -> mService.startVpnProfile(notMyVpnPkg)); + } + + @Test + public void testStopVpnProfileFromDiffPackage() throws Exception { + final String notMyVpnPkg = "com.not.my.vpn"; + assertThrows(SecurityException.class, () -> mService.stopVpnProfile(notMyVpnPkg)); + } + @Test public void testUidUpdateChangesInterfaceFilteringRule() throws Exception { LinkProperties lp = new LinkProperties(); -- cgit v1.2.3 From 6cdc54bee9d86d3b2de929a99e13da42d088a3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timi=20Rautam=C3=A4ki?= Date: Sun, 26 Sep 2021 18:13:32 +0000 Subject: SystemUI: fix volume dialog position on TV Expanded volume panel didn't respect the TV dimensions. Bring them back conditionally. Change-Id: I1d0c884726b74cc38fb825aebdff2484285789c2 --- .../src/com/android/systemui/volume/VolumeDialogImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index b0ecd105b349..dce766f49e2f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -244,7 +244,11 @@ public class VolumeDialogImpl implements VolumeDialog, private void initDialog() { // Gravitate various views left/right depending on panel placement setting. - final int panelGravity = mVolumePanelOnLeft ? Gravity.LEFT : Gravity.RIGHT; + int panelGravity = + mContext.getResources().getInteger(R.integer.volume_dialog_gravity); + if (!mShowActiveStreamOnly) { + panelGravity = mVolumePanelOnLeft ? Gravity.LEFT : Gravity.RIGHT; + } mConfigurableTexts = new ConfigurableTexts(mContext); mHovering = false; @@ -291,7 +295,8 @@ public class VolumeDialogImpl implements VolumeDialog, FrameLayout.LayoutParams dialogViewLP = (FrameLayout.LayoutParams) mDialogView.getLayoutParams(); - dialogViewLP.gravity = Gravity.CENTER_VERTICAL; + dialogViewLP.gravity = mShowActiveStreamOnly ? panelGravity + : Gravity.CENTER_VERTICAL; mDialogView.setLayoutParams(dialogViewLP); mDialogMainView = mDialog.findViewById(R.id.main); -- cgit v1.2.3 From cf0606c2f73d42e8fd9081f7b635e10b575a0b0c Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Fri, 1 Mar 2019 11:29:00 -0600 Subject: [1/2] Add battery info to tvsettings device info Change-Id: I98abe338d77b56f2a851e0f093a44d6911b9009b (cherry picked from commit 5cd43b356ebfe1b4018fbf9fbe5dd551e9a82b78) (cherry picked from commit cceffef44a297cafc671590fb541f5c5171d6ee6) --- .../AbstractBatteryStatusPreferenceController.java | 157 +++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBatteryStatusPreferenceController.java diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBatteryStatusPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBatteryStatusPreferenceController.java new file mode 100644 index 000000000000..d86dae8fd8ba --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBatteryStatusPreferenceController.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 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.settingslib.deviceinfo; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.text.format.DateUtils; + +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.R; + +import java.lang.ref.WeakReference; + +/** + * Preference controller for battery status + */ +public abstract class AbstractBatteryStatusPreferenceController extends AbstractPreferenceController + implements LifecycleObserver, OnStart, OnStop { + + @VisibleForTesting + static final String KEY_BATTERY_STATUS = "battery_status"; + private static final int EVENT_UPDATE_BATTERY = 700; + + private Preference mBatteryStatus; + private Handler mHandler; + private Context mContext; + + public AbstractBatteryStatusPreferenceController(Context context, Lifecycle lifecycle) { + super(context); + mContext = context; + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public void onStart() { + getHandler().sendEmptyMessage(EVENT_UPDATE_BATTERY); + } + + @Override + public void onStop() { + getHandler().removeMessages(EVENT_UPDATE_BATTERY); + } + + @Override + public boolean isAvailable() { + Intent intent = mContext.registerReceiver(null, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + return intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false); + } + + @Override + public String getPreferenceKey() { + return KEY_BATTERY_STATUS; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mBatteryStatus = screen.findPreference(KEY_BATTERY_STATUS); + updateBattery(); + } + + private Handler getHandler() { + if (mHandler == null) { + mHandler = new MyHandler(this); + } + return mHandler; + } + + private void updateBattery() { + Intent intent = mContext.registerReceiver(null, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + if (mBatteryStatus != null) { + String batterystatus = mContext.getString(R.string.battery_info_status_unknown); + String batterylevel = Integer.toString(Math.round(100.f + * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100) + / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100))) + "%"; + + switch (intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)) { + case BatteryManager.BATTERY_STATUS_CHARGING: + batterystatus = mContext.getString(R.string.battery_info_status_charging); + break; + case BatteryManager.BATTERY_STATUS_DISCHARGING: + batterystatus = mContext.getString(R.string.battery_info_status_discharging); + break; + case BatteryManager.BATTERY_STATUS_FULL: + batterystatus = mContext.getString(R.string.battery_info_status_full); + break; + case BatteryManager.BATTERY_STATUS_NOT_CHARGING: + batterystatus = mContext.getString(R.string.battery_info_status_not_charging); + break; + case BatteryManager.BATTERY_STATUS_UNKNOWN: + default: + break; + } + + mBatteryStatus.setSummary(batterylevel + " - " + batterystatus); + } + } + + private static class MyHandler extends Handler { + private WeakReference mStatus; + + public MyHandler(AbstractBatteryStatusPreferenceController activity) { + mStatus = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + AbstractBatteryStatusPreferenceController status = mStatus.get(); + if (status == null) { + return; + } + + switch (msg.what) { + case EVENT_UPDATE_BATTERY: + status.updateBattery(); + sendEmptyMessageDelayed(EVENT_UPDATE_BATTERY, 1000); + break; + + default: + throw new IllegalStateException("Unknown message " + msg.what); + } + } + } +} -- cgit v1.2.3