summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/media/framework/java/android/media/MediaController2.java1
-rw-r--r--api/current.txt5
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp2
-rw-r--r--core/java/android/hardware/face/FaceSensorProperties.java9
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorProperties.java7
-rw-r--r--core/java/android/view/ViewDebug.java2
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java5
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java11
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java6
-rw-r--r--media/java/android/media/browse/MediaBrowser.java2
-rw-r--r--non-updatable-api/current.txt5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java8
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java52
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java232
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java1975
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java5
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java1731
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java55
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java22
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java17
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java19
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/Face10.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java6
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java114
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java27
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java9
-rw-r--r--services/core/java/com/android/server/pm/permission/DevicePermissionState.java77
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java1077
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionState.java129
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionsState.java22
-rw-r--r--services/core/java/com/android/server/pm/permission/UidPermissionState.java574
-rw-r--r--services/core/java/com/android/server/pm/permission/UserPermissionState.java103
-rw-r--r--services/core/java/com/android/server/wm/TaskTapPointerEventListener.java12
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java75
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java44
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java5
-rw-r--r--tests/SilkFX/res/layout/activity_glass.xml30
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt38
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt100
60 files changed, 4419 insertions, 2523 deletions
diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
index d059c670ccb6..159e8e551d11 100644
--- a/apex/media/framework/java/android/media/MediaController2.java
+++ b/apex/media/framework/java/android/media/MediaController2.java
@@ -594,7 +594,6 @@ public class MediaController2 implements AutoCloseable {
if (DEBUG) {
Log.d(TAG, "onServiceConnected " + name + " " + this);
}
- // Sanity check
if (!mSessionToken.getPackageName().equals(name.getPackageName())) {
Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName()
+ " but is connected to " + name);
diff --git a/api/current.txt b/api/current.txt
index 723ffec5916b..6cc6956b8aba 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -46824,6 +46824,7 @@ package android.telephony {
field public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
field public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
field public static final String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
+ field public static final String KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL = "hide_tty_hco_vco_with_rtt";
field public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS = "ignore_data_enabled_changed_for_video_calls";
field public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool";
field public static final String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
@@ -46890,7 +46891,11 @@ package android.telephony {
field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
+ field public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
+ field public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
+ field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+ field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 46c377037542..8334b6b4db90 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -315,6 +315,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
}
int StatsPullerManager::ForceClearPullerCache() {
+ std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ForceClearCache();
@@ -323,6 +324,7 @@ int StatsPullerManager::ForceClearPullerCache() {
}
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java
index 1015724a485d..23ad9353a206 100644
--- a/core/java/android/hardware/face/FaceSensorProperties.java
+++ b/core/java/android/hardware/face/FaceSensorProperties.java
@@ -40,21 +40,27 @@ public class FaceSensorProperties implements Parcelable {
* from above the HAL.
*/
public final boolean supportsSelfIllumination;
+ /**
+ * Maximum number of enrollments a user/profile can have.
+ */
+ public final int maxTemplatesAllowed;
/**
* Initializes SensorProperties with specified values
*/
public FaceSensorProperties(int sensorId, boolean supportsFaceDetection,
- boolean supportsSelfIllumination) {
+ boolean supportsSelfIllumination, int maxTemplatesAllowed) {
this.sensorId = sensorId;
this.supportsFaceDetection = supportsFaceDetection;
this.supportsSelfIllumination = supportsSelfIllumination;
+ this.maxTemplatesAllowed = maxTemplatesAllowed;
}
protected FaceSensorProperties(Parcel in) {
sensorId = in.readInt();
supportsFaceDetection = in.readBoolean();
supportsSelfIllumination = in.readBoolean();
+ maxTemplatesAllowed = in.readInt();
}
public static final Creator<FaceSensorProperties> CREATOR =
@@ -80,5 +86,6 @@ public class FaceSensorProperties implements Parcelable {
dest.writeInt(sensorId);
dest.writeBoolean(supportsFaceDetection);
dest.writeBoolean(supportsSelfIllumination);
+ dest.writeInt(maxTemplatesAllowed);
}
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index b28551c52b9e..2fd006809046 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -48,21 +48,25 @@ public class FingerprintSensorProperties implements Parcelable {
// IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
// cannot be checked
public final boolean resetLockoutRequiresHardwareAuthToken;
+ // Maximum number of enrollments a user/profile can have.
+ public final int maxTemplatesAllowed;
/**
* Initializes SensorProperties with specified values
*/
public FingerprintSensorProperties(int sensorId, @SensorType int sensorType,
- boolean resetLockoutRequiresHardwareAuthToken) {
+ boolean resetLockoutRequiresHardwareAuthToken, int maxTemplatesAllowed) {
this.sensorId = sensorId;
this.sensorType = sensorType;
this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+ this.maxTemplatesAllowed = maxTemplatesAllowed;
}
protected FingerprintSensorProperties(Parcel in) {
sensorId = in.readInt();
sensorType = in.readInt();
resetLockoutRequiresHardwareAuthToken = in.readBoolean();
+ maxTemplatesAllowed = in.readInt();
}
public static final Creator<FingerprintSensorProperties> CREATOR =
@@ -88,5 +92,6 @@ public class FingerprintSensorProperties implements Parcelable {
dest.writeInt(sensorId);
dest.writeInt(sensorType);
dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
+ dest.writeInt(maxTemplatesAllowed);
}
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 4303d705f945..100b4fdbf446 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -531,7 +531,7 @@ public class ViewDebug {
@UnsupportedAppUsage
static void dispatchCommand(View view, String command, String parameters,
OutputStream clientStream) throws IOException {
- // Paranoid but safe...
+ // Just being cautious...
view = view.getRootView();
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 8b4fddbec03c..6314fcbde333 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -395,6 +395,11 @@ public final class SystemUiDeviceConfigFlags {
public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";
/**
+ * (boolean) Whether to enable stashing for PIP.
+ */
+ public static final String PIP_STASHING = "pip_stashing";
+
+ /**
* (float) Bottom height in DP for Back Gesture.
*/
public static final String BACK_GESTURE_BOTTOM_HEIGHT = "back_gesture_bottom_height";
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 41bf74ce9d61..e58990eff2c8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -73,6 +73,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.KeyValueListParser;
import android.util.Log;
@@ -14126,7 +14127,11 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void dumpConstantsLocked(PrintWriter pw) {
- mConstants.dumpLocked(pw);
+ final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
+ iPw.println("BatteryStats constants:");
+ iPw.increaseIndent();
+ mConstants.dumpLocked(iPw);
+ iPw.decreaseIndent();
}
@GuardedBy("this")
@@ -16077,6 +16082,7 @@ public class BatteryStatsImpl extends BatteryStats {
mCameraOnTimer.logState(pr, " ");
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
+
pw.print("Total cpu time reads: ");
pw.println(mNumSingleUidCpuTimeReads);
pw.print("Batched cpu time reads: ");
@@ -16087,5 +16093,8 @@ public class BatteryStatsImpl extends BatteryStats {
pw.println(mNumAllUidCpuTimeReads);
pw.print("UIDs removed since the later of device start or stats reset: ");
pw.println(mNumUidsRemoved);
+
+ pw.println();
+ dumpConstantsLocked(pw);
}
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ac08d96ab303..050c1c4b4df5 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -239,7 +239,9 @@
<!-- Old synonym for "privileged". Deprecated in API level 23. -->
<flag name="system" value="0x10" />
<!-- Additional flag from base permission type: this permission can also
- (optionally) be granted to development applications. -->
+ (optionally) be granted to development applications. Although undocumented, the
+ permission state used to be shared by all users (including future users), but it is
+ managed per-user since API level 31. -->
<flag name="development" value="0x20" />
<!-- Additional flag from base permission type: this permission is closely
associated with an app op for controlling access. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index d0051db9e9fc..9047b71253da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -46,6 +46,12 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
final SurfaceControl.Transaction t = mTransactionPool.acquire();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
+ // Reset several properties back to fullscreen (PiP, for example, leaves all these
+ // properties in a bad state).
+ t.setPosition(leash, 0, 0);
+ t.setWindowCrop(leash, null);
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
t.show(leash);
t.apply();
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 029e61492b6d..011e835e02ec 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -210,7 +210,7 @@ public final class MediaBrowser {
public void disconnect() {
// It's ok to call this any state, because allowing this lets apps not have
// to check isConnected() unnecessarily. They won't appreciate the extra
- // assertions for this. We do everything we can here to go back to a sane state.
+ // assertions for this. We do everything we can here to go back to a valid state.
mState = CONNECT_STATE_DISCONNECTING;
mHandler.post(new Runnable() {
@Override
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 26a0ad917c99..c0509445a54a 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -44967,6 +44967,7 @@ package android.telephony {
field public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
field public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
field public static final String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
+ field public static final String KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL = "hide_tty_hco_vco_with_rtt";
field public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS = "ignore_data_enabled_changed_for_video_calls";
field public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool";
field public static final String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
@@ -45033,7 +45034,11 @@ package android.telephony {
field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
+ field public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
+ field public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
+ field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+ field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index e316139326a9..078196e18b88 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -305,14 +305,14 @@ public class SystemBarConfigs {
e.printStackTrace();
}
- if (!mTopNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
- TopNotificationPanelViewMediator.class)) {
+ if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom(
+ notificationPanelMediatorUsed)) {
throw new RuntimeException(
"Top System Bar must be enabled to use " + notificationPanelMediatorName);
}
- if (!mBottomNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
- BottomNotificationPanelViewMediator.class)) {
+ if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom(
+ notificationPanelMediatorUsed)) {
throw new RuntimeException("Bottom System Bar must be enabled to use "
+ notificationPanelMediatorName);
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
index 7eb41de82ae0..0c62f8b4a22b 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
@@ -33,7 +33,14 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.car.notification.NotificationPanelViewController;
+import com.android.systemui.car.notification.NotificationPanelViewMediator;
+import com.android.systemui.car.notification.PowerManagerHelper;
+import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Test;
@@ -55,8 +62,6 @@ public class SystemBarConfigsTest extends SysuiTestCase {
private SystemBarConfigs mSystemBarConfigs;
@Mock
private Resources mResources;
- @Mock
- private CarNavigationBarView mCarNavigationBarView;
@Before
public void setUp() {
@@ -109,6 +114,33 @@ public class SystemBarConfigsTest extends SysuiTestCase {
}
@Test
+ public void onInit_topNotifPanelViewMediatorUsed_topBarEnabled_doesNotThrowException() {
+ when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(true);
+ when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+ TestTopNotificationPanelViewMediator.class.getName());
+
+ mSystemBarConfigs = new SystemBarConfigs(mResources);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void onInit_topNotifPanelViewMediatorUsed_topBarNotEnabled_throwsRuntimeException() {
+ when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+ when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+ TestTopNotificationPanelViewMediator.class.getName());
+
+ mSystemBarConfigs = new SystemBarConfigs(mResources);
+ }
+
+ @Test
+ public void onInit_notificationPanelViewMediatorUsed_topBarNotEnabled_doesNotThrowException() {
+ when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+ when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+ NotificationPanelViewMediator.class.getName());
+
+ mSystemBarConfigs = new SystemBarConfigs(mResources);
+ }
+
+ @Test
public void getTopSystemBarLayoutParams_topBarEnabled_returnsTopSystemBarLayoutParams() {
mSystemBarConfigs = new SystemBarConfigs(mResources);
WindowManager.LayoutParams lp = mSystemBarConfigs.getLayoutParamsBySide(
@@ -239,4 +271,20 @@ public class SystemBarConfigsTest extends SysuiTestCase {
when(mResources.getBoolean(R.bool.config_hideRightSystemBarForKeyboard)).thenReturn(
false);
}
+
+ // Intentionally using a subclass of TopNotificationPanelViewMediator for testing purposes to
+ // ensure that OEM's will be able to implement and use their own NotificationPanelViewMediator.
+ private class TestTopNotificationPanelViewMediator extends
+ TopNotificationPanelViewMediator {
+ TestTopNotificationPanelViewMediator(
+ CarNavigationBarController carNavigationBarController,
+ NotificationPanelViewController notificationPanelViewController,
+ PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
+ CarDeviceProvisionedController carDeviceProvisionedController,
+ ConfigurationController configurationController) {
+ super(carNavigationBarController, notificationPanelViewController, powerManagerHelper,
+ broadcastDispatcher, carDeviceProvisionedController, configurationController);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index bba69f29a290..aad0d3af6626 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -35,7 +35,6 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
import com.android.settingslib.Utils;
import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
/**
* Preference to display a WifiEntry in a wifi picker.
@@ -138,11 +137,7 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt
public void refresh() {
setTitle(mWifiEntry.getTitle());
final int level = mWifiEntry.getLevel();
- final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo();
- boolean showX = false;
- if (connectedInfo != null) {
- showX = !connectedInfo.isDefaultNetwork || !connectedInfo.isValidated;
- }
+ final boolean showX = mWifiEntry.shouldShowXLevelIcon();
if (level != mLevel || showX != mShowX) {
mLevel = level;
mShowX = showX;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index 40af7dc797b3..c21830b28e3a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -17,7 +17,6 @@ package com.android.settingslib.wifi;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -30,7 +29,6 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
import org.junit.Before;
import org.junit.Test;
@@ -179,43 +177,9 @@ public class WifiEntryPreferenceTest {
}
@Test
- public void levelChanged_notDefaultWifiRefresh_shouldUpdateLevelIcon() {
+ public void levelChanged_showXWifiRefresh_shouldUpdateLevelIcon() {
final List<Drawable> iconList = new ArrayList<>();
- final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class);
- mockConnectedInfo.isDefaultNetwork = false;
- when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
-
- assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
- mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
- }
-
- @Test
- public void levelChanged_notValidatedWifiRefresh_shouldUpdateLevelIcon() {
- final List<Drawable> iconList = new ArrayList<>();
- final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class);
- mockConnectedInfo.isValidated = false;
- when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo);
+ when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
final WifiEntryPreference pref =
new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index dcee2a5b4b20..e24121928808 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -58,6 +58,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
private static final int LEAVE_PIP_DURATION = 300;
private static final int SHIFT_DURATION = 300;
+ private static final float STASH_RATIO = 0.25f;
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 2f;
@@ -120,6 +121,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
private PhysicsAnimator.FlingConfig mFlingConfigX;
private PhysicsAnimator.FlingConfig mFlingConfigY;
+ /** FlingConfig instances proviced to PhysicsAnimator for stashing. */
+ private PhysicsAnimator.FlingConfig mStashConfigX;
/** SpringConfig to use for fling-then-spring animations. */
private final PhysicsAnimator.SpringConfig mSpringConfig =
@@ -383,6 +386,21 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
void flingToSnapTarget(
float velocityX, float velocityY,
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
+ movetoTarget(velocityX, velocityY, updateAction, endAction, false /* isStash */);
+ }
+
+ /**
+ * Stash PiP to the closest edge.
+ */
+ void stashToEdge(
+ float velocityX, float velocityY,
+ @Nullable Runnable updateAction, @Nullable Runnable endAction) {
+ movetoTarget(velocityX, velocityY, updateAction, endAction, true /* isStash */);
+ }
+
+ private void movetoTarget(
+ float velocityX, float velocityY,
+ @Nullable Runnable updateAction, @Nullable Runnable endAction, boolean isStash) {
// If we're flinging to a snap target now, we're not springing to catch up to the touch
// location now.
mSpringingToTouch = false;
@@ -391,8 +409,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
.spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
.spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
.flingThenSpring(
- FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
- true /* flingMustReachMinOrMax */)
+ FloatProperties.RECT_X, velocityX, isStash ? mStashConfigX : mFlingConfigX,
+ mSpringConfig, true /* flingMustReachMinOrMax */)
.flingThenSpring(
FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
.withEndActions(endAction);
@@ -402,7 +420,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
(target, values) -> updateAction.run());
}
- final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
+ final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
+ final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left;
+ final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right;
+
+ final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
final float estimatedFlingYEndValue =
PhysicsAnimator.estimateFlingEndValue(
mTemporaryBounds.top, velocityY, mFlingConfigY);
@@ -506,6 +528,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
mFlingConfigY = new PhysicsAnimator.FlingConfig(
DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
+ final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
+ mStashConfigX = new PhysicsAnimator.FlingConfig(
+ DEFAULT_FRICTION, mMovementBounds.left - offset, mMovementBounds.right + offset);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 97b3484292c6..858683c4e2d4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip.phone;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
@@ -33,6 +34,7 @@ import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
import android.os.Handler;
import android.os.RemoteException;
+import android.provider.DeviceConfig;
import android.util.Log;
import android.util.Size;
import android.view.Gravity;
@@ -102,6 +104,13 @@ public class PipTouchHandler {
private boolean mShowPipMenuOnAnimationEnd = false;
/**
+ * Whether PIP stash is enabled or not. When enabled, if at the time of fling-release the
+ * PIP bounds is outside the left/right edge of the screen, it will be shown in "stashed" mode,
+ * where PIP will only show partially.
+ */
+ private boolean mEnableStash = false;
+
+ /**
* MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
* PIP.
*/
@@ -306,6 +315,22 @@ public class PipTouchHandler {
});
mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
+
+ mEnableStash = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ PIP_STASHING,
+ /* defaultValue = */ false);
+ deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ context.getMainExecutor(),
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(PIP_STASHING)) {
+ mEnableStash = properties.getBoolean(
+ PIP_STASHING, /* defaultValue = */ false);
+ }
+ }
+ });
}
private void reloadResources() {
@@ -986,9 +1011,20 @@ public class PipTouchHandler {
// Reset the touch state on up before the fling settles
mTouchState.reset();
- mMotionHelper.flingToSnapTarget(vel.x, vel.y,
- PipTouchHandler.this::updateDismissFraction /* updateAction */,
- this::flingEndAction /* endAction */);
+ final Rect animatingBounds = mMotionHelper.getPossiblyAnimatingBounds();
+ // If User releases the PIP window while it's out of the display bounds, put
+ // PIP into stashed mode.
+ if (mEnableStash
+ && (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right
+ || animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) {
+ mMotionHelper.stashToEdge(vel.x, vel.y,
+ PipTouchHandler.this::updateDismissFraction /* updateAction */,
+ this::flingEndAction /* endAction */);
+ } else {
+ mMotionHelper.flingToSnapTarget(vel.x, vel.y,
+ PipTouchHandler.this::updateDismissFraction /* updateAction */,
+ this::flingEndAction /* endAction */);
+ }
} else if (mTouchState.isDoubleTap()) {
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 82c1f243dcdb..fd0476b76a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -110,4 +110,11 @@ public abstract class ListEntry {
mPreviousAttachState.clone(mAttachState);
mAttachState.reset();
}
+
+ /**
+ * True if this entry was attached in the last pass, else false.
+ */
+ public boolean wasAttachedInPreviousPass() {
+ return getPreviousAttachState().getParent() != null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index df63fec62e4e..a0ef1b674af3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -513,26 +513,64 @@ public class ShadeListBuilder implements Dumpable {
}
}
- private void stabilizeGroupingNotifs(List<ListEntry> list) {
+ private void stabilizeGroupingNotifs(List<ListEntry> topLevelList) {
if (mNotifStabilityManager == null) {
return;
}
- for (int i = 0; i < list.size(); i++) {
- final ListEntry tle = list.get(i);
- if (tle.getPreviousAttachState().getParent() == null) {
- continue; // new entries are allowed
+ for (int i = 0; i < topLevelList.size(); i++) {
+ final ListEntry tle = topLevelList.get(i);
+ if (tle instanceof GroupEntry) {
+ // maybe put children back into their old group (including moving back to top-level)
+ GroupEntry groupEntry = (GroupEntry) tle;
+ List<NotificationEntry> children = groupEntry.getRawChildren();
+ for (int j = 0; j < groupEntry.getChildren().size(); j++) {
+ if (maybeSuppressGroupChange(children.get(j), topLevelList)) {
+ // child was put back into its previous group, so we remove it from this
+ // group
+ children.remove(j);
+ j--;
+ }
+ }
+ } else {
+ // maybe put top-level-entries back into their previous groups
+ if (maybeSuppressGroupChange(tle.getRepresentativeEntry(), topLevelList)) {
+ // entry was put back into its previous group, so we remove it from the list of
+ // top-level-entries
+ topLevelList.remove(i);
+ i--;
+ }
}
+ }
+ }
- final GroupEntry prevParent = tle.getPreviousAttachState().getParent();
- final GroupEntry assignedParent = tle.getParent();
- if (prevParent != assignedParent) {
- if (!mNotifStabilityManager.isGroupChangeAllowed(tle.getRepresentativeEntry())) {
- tle.getAttachState().getSuppressedChanges().setParent(assignedParent);
- tle.setParent(prevParent);
+ /**
+ * Returns true if the group change was suppressed, else false
+ */
+ private boolean maybeSuppressGroupChange(NotificationEntry entry, List<ListEntry> out) {
+ if (!entry.wasAttachedInPreviousPass()) {
+ return false; // new entries are allowed
+ }
+
+ final GroupEntry prevParent = entry.getPreviousAttachState().getParent();
+ final GroupEntry assignedParent = entry.getParent();
+ if (prevParent != assignedParent
+ && !mNotifStabilityManager.isGroupChangeAllowed(entry.getRepresentativeEntry())) {
+ entry.getAttachState().getSuppressedChanges().setParent(assignedParent);
+ entry.setParent(prevParent);
+ if (prevParent == ROOT_ENTRY) {
+ out.add(entry);
+ } else if (prevParent != null) {
+ prevParent.addChild(entry);
+ if (!mGroups.containsKey(prevParent.getKey())) {
+ mGroups.put(prevParent.getKey(), prevParent);
}
}
+
+ return true;
}
+
+ return false;
}
private void promoteNotifs(List<ListEntry> list) {
@@ -577,6 +615,17 @@ public class ShadeListBuilder implements Dumpable {
} else if (group.getSummary() == null
|| children.size() < MIN_CHILDREN_FOR_GROUP) {
+
+ if (group.getSummary() != null
+ && group.wasAttachedInPreviousPass()
+ && mNotifStabilityManager != null
+ && !mNotifStabilityManager.isGroupChangeAllowed(group.getSummary())) {
+ // if this group was previously attached and group changes aren't
+ // allowed, keep it around until group changes are allowed again
+ group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
+ continue;
+ }
+
// If the group doesn't provide a summary or is too small, ignore it and add
// its children (if any) directly to top-level.
@@ -718,6 +767,12 @@ public class ShadeListBuilder implements Dumpable {
curr.getParent());
}
+ if (curr.getSuppressedChanges().getWasPruneSuppressed()) {
+ mLogger.logGroupPruningSuppressed(
+ mIterationCount,
+ curr.getParent());
+ }
+
if (curr.getExcludingFilter() != prev.getExcludingFilter()) {
mLogger.logFilterChanged(
mIterationCount,
@@ -867,9 +922,9 @@ public class ShadeListBuilder implements Dumpable {
NotifSection finalSection = newSection;
- // are we changing sections of this entry?
+ // have we seen this entry before and are we changing its section?
if (mNotifStabilityManager != null
- && prevAttachState.getParent() != null
+ && entry.wasAttachedInPreviousPass()
&& newSection != prevAttachState.getSection()) {
// are section changes allowed?
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
index 3eb2e610f329..584563b6c388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
@@ -24,28 +24,37 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.NotifS
*/
data class SuppressedAttachState private constructor(
/**
- * Null if not attached to the current shade list. If top-level, then the shade list root. If
- * part of a group, then that group's GroupEntry.
+ * The suppressed section assignment for this ListEntry.
+ * Null if no section change was suppressed.
+ */
+ var section: NotifSection?,
+
+ /**
+ * The suppressed parent assignment for this ListEntry.
+ * - Null if no parent change was suppressed.
+ * - Root if suppressing group change to top-level
+ * - GroupEntry if suppressing group change to a different group
*/
var parent: GroupEntry?,
/**
- * The assigned section for this ListEntry. If the child of the group, this will be the
- * parent's section. Null if not attached to the list.
+ * Whether the ListEntry would have been pruned had its group change not been suppressed.
*/
- var section: NotifSection?
+ var wasPruneSuppressed: Boolean
) {
/** Copies the state of another instance. */
fun clone(other: SuppressedAttachState) {
parent = other.parent
section = other.section
+ wasPruneSuppressed = other.wasPruneSuppressed
}
/** Resets back to a "clean" state (the same as created by the factory method) */
fun reset() {
parent = null
section = null
+ wasPruneSuppressed = false
}
companion object {
@@ -53,7 +62,8 @@ data class SuppressedAttachState private constructor(
fun create(): SuppressedAttachState {
return SuppressedAttachState(
null,
- null)
+ null,
+ false)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 08030f8201a2..5d6c0437ce9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -110,7 +110,7 @@ public class VisualStabilityCoordinator implements Coordinator {
public boolean isGroupChangeAllowed(NotificationEntry entry) {
final boolean isGroupChangeAllowedForEntry =
mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey());
- mIsSuppressingGroupChange |= isGroupChangeAllowedForEntry;
+ mIsSuppressingGroupChange |= !isGroupChangeAllowedForEntry;
return isGroupChangeAllowedForEntry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 9ee7db738c20..5a35127397b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -174,8 +174,19 @@ class ShadeListBuilderLogger @Inject constructor(
str1 = suppressedParent?.key
str2 = keepingParent?.key
}, {
- "(Build $long1) Change of parent to '$str1' suppressed; " +
- "keeping parent '$str2'"
+ "(Build $long1) Change of parent to '$str1' suppressed; keeping parent '$str2'"
+ })
+ }
+
+ fun logGroupPruningSuppressed(
+ buildId: Int,
+ keepingParent: GroupEntry?
+ ) {
+ buffer.log(TAG, INFO, {
+ int1 = buildId
+ str1 = keepingParent?.key
+ }, {
+ "(Build $long1) Group pruning suppressed; keeping parent '$str1'"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index c1f468a3072f..e784ec62c8e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -56,7 +56,7 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager {
return false;
}
- return entry.getParent().getChildren().size() == 1;
+ return !isGroupSummary(entry) && entry.getParent().getChildren().size() == 1;
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index d8eecef024ce..3aba7ca1ad7c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -76,6 +76,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject;
@@ -829,7 +830,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
class C implements Callbacks {
- private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
+ private final Map<Callbacks, Handler> mCallbackMap = new ConcurrentHashMap<>();
public void add(Callbacks callback, Handler handler) {
if (callback == null || handler == null) throw new IllegalArgumentException();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 11920233e403..d93cc05c57db 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -182,7 +182,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// IBiometricsFace@1.0 does not support detection, only authentication.
when(mFaceSensorProperties.isEmpty()).thenReturn(false);
when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorProperties(0 /* id */,
- false /* supportsFaceDetection */, true /* supportsSelfIllumination */));
+ false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
+ 1 /* maxTemplatesAllowed */));
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 8acb705c744d..2ce22a6f71c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.util.time.FakeSystemClock;
@@ -964,6 +965,198 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
+ public void testStabilizeGroupsDoesNotAllowGrouping() {
+ // GIVEN one group child without a summary yet
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // GIVEN visual stability manager doesn't allow any group changes
+ mListBuilder.setNotifStabilityManager(
+ new TestableStabilityManager().setAllowGroupChanges(false));
+
+ // WHEN we run the pipeline with the addition of a group summary & child
+ addGroupSummary(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // THEN all notifications are top-level and the summary doesn't show yet
+ // because group changes aren't allowed by the stability manager
+ verifyBuiltList(
+ notif(0),
+ notif(2)
+ );
+ }
+
+ @Test
+ public void testStabilizeGroupsAllowsGroupingAllNewNotifications() {
+ // GIVEN visual stability manager doesn't allow any group changes
+ mListBuilder.setNotifStabilityManager(
+ new TestableStabilityManager().setAllowGroupChanges(false));
+
+ // WHEN we run the pipeline with all new notification groups
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+ addGroupSummary(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+ addGroupSummary(3, PACKAGE_2, GROUP_2);
+ addGroupChild(4, PACKAGE_2, GROUP_2);
+ addGroupChild(5, PACKAGE_2, GROUP_2);
+
+ dispatchBuild();
+
+ // THEN all notifications are grouped since they're all new
+ verifyBuiltList(
+ group(
+ summary(1),
+ child(0),
+ child(2)
+ ),
+ group(
+ summary(3),
+ child(4),
+ child(5)
+ )
+ );
+ }
+
+
+ @Test
+ public void testStabilizeGroupsAllowsGroupingOnlyNewNotifications() {
+ // GIVEN one group child without a summary yet
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // GIVEN visual stability manager doesn't allow any group changes
+ mListBuilder.setNotifStabilityManager(
+ new TestableStabilityManager().setAllowGroupChanges(false));
+
+ // WHEN we run the pipeline with the addition of a group summary & child
+ addGroupSummary(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+ addGroupSummary(3, PACKAGE_2, GROUP_2);
+ addGroupChild(4, PACKAGE_2, GROUP_2);
+ addGroupChild(5, PACKAGE_2, GROUP_2);
+
+ dispatchBuild();
+
+ // THEN all notifications are top-level and the summary doesn't show yet
+ // because group changes aren't allowed by the stability manager
+ verifyBuiltList(
+ notif(0),
+ group(
+ summary(3),
+ child(4),
+ child(5)
+ ),
+ notif(2)
+ );
+ }
+
+ @Test
+ public void testStabilizeGroupsHidesGroupSummary() {
+ // GIVEN one group child with a summary
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+ addGroupSummary(1, PACKAGE_1, GROUP_1);
+
+ dispatchBuild(); // group summary is hidden because it needs at least 2 children to group
+
+ // GIVEN visual stability manager doesn't allow any group changes
+ mListBuilder.setNotifStabilityManager(
+ new TestableStabilityManager().setAllowGroupChanges(false));
+
+ // WHEN we run the pipeline with the addition of a child
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // THEN the children notifications are top-level and the summary still doesn't show yet
+ // because group changes aren't allowed by the stability manager
+ verifyBuiltList(
+ notif(0),
+ notif(2)
+ );
+ }
+
+ @Test
+ public void testStabilizeGroupsDelayedSummaryRendersAllNotifsTopLevel() {
+ // GIVEN group children posted without a summary
+ addGroupChild(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+ addGroupChild(3, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // GIVEN visual stability manager doesn't allow any group changes
+ final TestableStabilityManager stabilityManager =
+ new TestableStabilityManager().setAllowGroupChanges(false);
+ mListBuilder.setNotifStabilityManager(stabilityManager);
+
+ // WHEN the delayed summary is posted
+ addGroupSummary(4, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // THEN all entries are top-level since group changes aren't allowed
+ verifyBuiltList(
+ notif(0),
+ notif(1),
+ notif(2),
+ notif(3),
+ notif(4)
+ );
+
+ // WHEN visual stability manager allows group changes again
+ stabilityManager.setAllowGroupChanges(true);
+ stabilityManager.invalidateList();
+
+ // THEN entries are grouped
+ verifyBuiltList(
+ group(
+ summary(4),
+ child(0),
+ child(1),
+ child(2),
+ child(3)
+ )
+ );
+ }
+
+ @Test
+ public void testStabilizeSectionDisallowsNewSection() {
+ // GIVEN one non-default sections
+ final NotifSectioner originalSectioner = new PackageSectioner(PACKAGE_1);
+ mListBuilder.setSectioners(List.of(originalSectioner));
+
+ // GIVEN notifications that's sectioned by sectioner1
+ addNotif(0, PACKAGE_1);
+ dispatchBuild();
+ assertEquals(originalSectioner, mEntrySet.get(0).getSection().getSectioner());
+
+ // WHEN section changes aren't allowed
+ final TestableStabilityManager stabilityManager =
+ new TestableStabilityManager().setAllowSectionChanges(false);
+ mListBuilder.setNotifStabilityManager(stabilityManager);
+
+ // WHEN we try to change the section
+ final NotifSectioner newSectioner = new PackageSectioner(PACKAGE_1);
+ mListBuilder.setSectioners(List.of(newSectioner, originalSectioner));
+ dispatchBuild();
+
+ // THEN the section remains the same since section changes aren't allowed
+ assertEquals(originalSectioner, mEntrySet.get(0).getSection().getSectioner());
+
+ // WHEN section changes are allowed again
+ stabilityManager.setAllowSectionChanges(true);
+ stabilityManager.invalidateList();
+
+ // THEN the section updates
+ assertEquals(newSectioner, mEntrySet.get(0).getSection().getSectioner());
+ }
+
+ @Test
public void testDispatchListOnBeforeSort() {
// GIVEN a registered OnBeforeSortListener
RecordingOnBeforeSortListener listener =
@@ -999,8 +1192,8 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Test
public void testDispatchListOnBeforeRender() {
// GIVEN a registered OnBeforeRenderList
- RecordingOnBeforeRenderistener listener =
- new RecordingOnBeforeRenderistener();
+ RecordingOnBeforeRenderListener listener =
+ new RecordingOnBeforeRenderListener();
mListBuilder.addOnBeforeRenderListListener(listener);
// GIVEN some new notifs out of order
@@ -1450,7 +1643,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
}
- private static class RecordingOnBeforeRenderistener
+ private static class RecordingOnBeforeRenderListener
implements OnBeforeRenderListListener {
List<ListEntry> mEntriesReceived;
@@ -1460,6 +1653,39 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
}
+ private static class TestableStabilityManager extends NotifStabilityManager {
+ boolean mAllowGroupChanges = true;
+ boolean mAllowSectionChanges = true;
+
+ TestableStabilityManager() {
+ super("Test");
+ }
+
+ TestableStabilityManager setAllowGroupChanges(boolean allowGroupChanges) {
+ mAllowGroupChanges = allowGroupChanges;
+ return this;
+ }
+
+ TestableStabilityManager setAllowSectionChanges(boolean allowSectionChanges) {
+ mAllowSectionChanges = allowSectionChanges;
+ return this;
+ }
+
+ @Override
+ public void onBeginRun() {
+ }
+
+ @Override
+ public boolean isGroupChangeAllowed(NotificationEntry entry) {
+ return mAllowGroupChanges;
+ }
+
+ @Override
+ public boolean isSectionChangeAllowed(NotificationEntry entry) {
+ return mAllowSectionChanges;
+ }
+ }
+
private static final String PACKAGE_1 = "com.test1";
private static final String PACKAGE_2 = "com.test2";
private static final String PACKAGE_3 = "org.test3";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 605b4d18d2f1..4edca7dd43d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -296,6 +296,22 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
@Test
+ public void testNotSuppressingGroupChangesAnymore_invalidationCalled() {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false);
+ setScreenOn(true);
+ setPanelExpanded(true);
+
+ assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false);
+
+ // invalidate is called because we were previously suppressing a group change
+ verifyInvalidateCalled(true);
+ }
+
+ @Test
public void testHeadsUp_allowedToChangeGroupAndSection() {
// GIVEN group + section changes disallowed
setScreenOn(true);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e6649660aa9..b55d55562110 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -97,7 +97,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
@@ -110,7 +109,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -135,7 +133,6 @@ import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToS
import android.Manifest;
import android.Manifest.permission;
-import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -265,7 +262,6 @@ import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.permission.PermissionManagerInternal.CheckPermissionDelegate;
import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
import android.server.ServerProtoEnums;
import android.sysprop.InitProperties;
@@ -392,11 +388,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
public class ActivityManagerService extends IActivityManager.Stub
@@ -422,7 +414,6 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final String TAG_POWER = TAG + POSTFIX_POWER;
static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
- static final String TAG_PSS = TAG + POSTFIX_PSS;
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
@@ -436,21 +427,9 @@ public class ActivityManagerService extends IActivityManager.Stub
"com.android.internal.intent.action.BUGREPORT_REQUESTED";
private static final String SHELL_APP_PACKAGE = "com.android.shell";
- /** Control over CPU and battery monitoring */
- // write battery stats every 30 minutes.
- static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
- static final boolean MONITOR_CPU_USAGE = true;
- // don't sample cpu less than every 5 seconds.
- static final long MONITOR_CPU_MIN_TIME = 5 * 1000;
- // wait possibly forever for next cpu sample.
- static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;
- static final boolean MONITOR_THREAD_CPU_USAGE = false;
-
// The flags that are set for all calls we make to the package manager.
public static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
- static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
-
static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
public static final String ANR_TRACE_DIR = "/data/anr";
@@ -493,37 +472,6 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
- /**
- * Broadcast sent when heap dump collection has been completed.
- */
- @BroadcastBehavior(includeBackground = true, protectedBroadcast = true)
- private static final String ACTION_HEAP_DUMP_FINISHED =
- "com.android.internal.intent.action.HEAP_DUMP_FINISHED";
-
- /**
- * The process we are reporting
- */
- private static final String EXTRA_HEAP_DUMP_PROCESS_NAME =
- "com.android.internal.extra.heap_dump.PROCESS_NAME";
-
- /**
- * The size limit the process reached.
- */
- private static final String EXTRA_HEAP_DUMP_SIZE_BYTES =
- "com.android.internal.extra.heap_dump.SIZE_BYTES";
-
- /**
- * Whether the user initiated the dump or not.
- */
- private static final String EXTRA_HEAP_DUMP_IS_USER_INITIATED =
- "com.android.internal.extra.heap_dump.IS_USER_INITIATED";
-
- /**
- * Optional name of package to directly launch.
- */
- private static final String EXTRA_HEAP_DUMP_REPORT_PACKAGE =
- "com.android.internal.extra.heap_dump.REPORT_PACKAGE";
-
// If set, we will push process association information in to procstats.
static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
@@ -556,7 +504,6 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
OomAdjuster mOomAdjuster;
- final LowMemDetector mLowMemDetector;
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
@@ -918,28 +865,11 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
- /**
- * Processes we want to collect PSS data from.
- */
- final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
-
- /**
- * Depth of overlapping activity-start PSS deferral notes
- */
- private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
-
private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
new ActivityMetricsLaunchObserver() {
@Override
public void onActivityLaunched(byte[] activity, int temperature) {
- // This is safe to force to the head of the queue because it relies only
- // on refcounting to track begin/end of deferrals, not on actual
- // message ordering. We don't care *what* activity is being
- // launched; only that we're doing so.
- if (mPssDeferralTime > 0) {
- final Message msg = mBgHandler.obtainMessage(DEFER_PSS_MSG);
- mBgHandler.sendMessageAtFrontOfQueue(msg);
- }
+ mAppProfiler.onActivityLaunched();
}
// The other observer methods are unused
@@ -964,45 +894,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
};
- /**
- * How long we defer PSS gathering while activities are starting, in milliseconds.
- * This is adjustable via DeviceConfig. If it is zero or negative, no PSS deferral
- * is done.
- */
- private volatile long mPssDeferralTime = 0;
- private static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
-
private boolean mBinderTransactionTrackingEnabled = false;
/**
- * Last time we requested PSS data of all processes.
- */
- long mLastFullPssTime = SystemClock.uptimeMillis();
-
- /**
- * If set, the next time we collect PSS data we should do a full collection
- * with data from native processes and the kernel.
- */
- boolean mFullPssPending = false;
-
- /**
- * Observe DeviceConfig changes to the PSS calculation interval
- */
- private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(Properties properties) {
- if (properties.getKeyset().contains(ACTIVITY_START_PSS_DEFER_CONFIG)) {
- mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "Activity-start PSS delay now "
- + mPssDeferralTime + " ms");
- }
- }
- }
- };
-
- /**
* Fingerprints (hashCode()) of stack traces that we've
* already logged DropBox entries for. Guarded by itself. If
* something (rogue user app) forces this over
@@ -1242,41 +1136,11 @@ public class ActivityManagerService extends IActivityManager.Stub
int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
/**
- * Allow the current computed overall memory level of the system to go down?
- * This is set to false when we are killing processes for reasons other than
- * memory management, so that the now smaller process list will not be taken as
- * an indication that memory is tighter.
- */
- boolean mAllowLowerMemLevel = false;
-
- /**
- * The last computed memory level, for holding when we are in a state that
- * processes are going away for other reasons.
- */
- int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
-
- /**
- * The last total number of process we have, to determine if changes actually look
- * like a shrinking number of process due to lower RAM.
- */
- int mLastNumProcesses;
-
- /**
* The uptime of the last time we performed idle maintenance.
*/
long mLastIdleTime = SystemClock.uptimeMillis();
/**
- * Total time spent with RAM that has been added in the past since the last idle time.
- */
- long mLowRamTimeSinceLastIdle = 0;
-
- /**
- * If RAM is currently low, when that horrible situation started.
- */
- long mLowRamStartTime = 0;
-
- /**
* For reporting to battery stats the current top application.
*/
private String mCurResumedPackage = null;
@@ -1295,13 +1159,6 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
boolean mSafeMode;
- /**
- * If true, we are running under a test environment so will sample PSS from processes
- * much more rapidly to try to collect better data when the tests are rapidly
- * running through apps.
- */
- boolean mTestPssMode = false;
-
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
@@ -1309,61 +1166,6 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- class ProfileData {
- private String mProfileApp = null;
- private ProcessRecord mProfileProc = null;
- private ProfilerInfo mProfilerInfo = null;
-
- void setProfileApp(String profileApp) {
- mProfileApp = profileApp;
- if (mAtmInternal != null) {
- mAtmInternal.setProfileApp(profileApp);
- }
- }
-
- String getProfileApp() {
- return mProfileApp;
- }
-
- void setProfileProc(ProcessRecord profileProc) {
- mProfileProc = profileProc;
- if (mAtmInternal != null) {
- mAtmInternal.setProfileProc(profileProc == null ? null
- : profileProc.getWindowProcessController());
- }
- }
-
- ProcessRecord getProfileProc() {
- return mProfileProc;
- }
-
- void setProfilerInfo(ProfilerInfo profilerInfo) {
- mProfilerInfo = profilerInfo;
- if (mAtmInternal != null) {
- mAtmInternal.setProfilerInfo(profilerInfo);
- }
- }
-
- ProfilerInfo getProfilerInfo() {
- return mProfilerInfo;
- }
- }
- final ProfileData mProfileData = new ProfileData();
-
- /**
- * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
- * is not null, this map is checked and the mapped agent installed during bind-time. Note:
- * A non-null agent in mProfileInfo overrides this.
- */
- private @Nullable Map<String, String> mAppAgentMap = null;
-
- int mProfileType = 0;
- final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
- String mMemWatchDumpProcName;
- Uri mMemWatchDumpUri;
- int mMemWatchDumpPid;
- int mMemWatchDumpUid;
- private boolean mMemWatchIsUserInitiated;
String mTrackAllocationApp = null;
String mNativeDebuggingApp = null;
@@ -1402,27 +1204,6 @@ public class ActivityManagerService extends IActivityManager.Stub
final AnrHelper mAnrHelper = new AnrHelper(this);
- /**
- * Runtime CPU use collection thread. This object's lock is used to
- * perform synchronization with the thread (notifying it to run).
- */
- final Thread mProcessCpuThread;
-
- /**
- * Used to collect per-process CPU use for ANRs, battery stats, etc.
- * Must acquire this object's lock when accessing it.
- * NOTE: this lock will be held while doing long operations (trawling
- * through all processes in /proc), so it should never be acquired by
- * any critical paths such as when holding the main activity manager lock.
- */
- final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
- MONITOR_THREAD_CPU_USAGE);
- final AtomicLong mLastCpuTime = new AtomicLong(0);
- final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
- final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
-
- long mLastWriteTime = 0;
-
/** Set to true after the system has finished booting. */
volatile boolean mBooted = false;
@@ -1561,6 +1342,23 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
+ final AppProfiler mAppProfiler;
+
+ private static final int INDEX_NATIVE_PSS = 0;
+ private static final int INDEX_NATIVE_SWAP_PSS = 1;
+ private static final int INDEX_NATIVE_RSS = 2;
+ private static final int INDEX_DALVIK_PSS = 3;
+ private static final int INDEX_DALVIK_SWAP_PSS = 4;
+ private static final int INDEX_DALVIK_RSS = 5;
+ private static final int INDEX_OTHER_PSS = 6;
+ private static final int INDEX_OTHER_SWAP_PSS = 7;
+ private static final int INDEX_OTHER_RSS = 8;
+ private static final int INDEX_TOTAL_PSS = 9;
+ private static final int INDEX_TOTAL_SWAP_PSS = 10;
+ private static final int INDEX_TOTAL_RSS = 11;
+ private static final int INDEX_TOTAL_NATIVE_PSS = 12;
+ private static final int INDEX_LAST = 13;
+
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1752,63 +1550,10 @@ public class ActivityManagerService extends IActivityManager.Stub
break;
}
case POST_DUMP_HEAP_NOTIFICATION_MSG: {
- final String procName;
- final int uid;
- final long memLimit;
- final String reportPackage;
- final boolean isUserInitiated;
- synchronized (ActivityManagerService.this) {
- uid = mMemWatchDumpUid;
- procName = mMemWatchDumpProcName;
- Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
- if (val == null) {
- val = mMemWatchProcesses.get(procName, 0);
- }
- if (val != null) {
- memLimit = val.first;
- reportPackage = val.second;
- } else {
- memLimit = 0;
- reportPackage = null;
- }
- isUserInitiated = mMemWatchIsUserInitiated;
-
- mMemWatchDumpUri = null;
- mMemWatchDumpProcName = null;
- mMemWatchDumpPid = -1;
- mMemWatchDumpUid = -1;
- }
- if (procName == null) {
- return;
- }
-
- if (DEBUG_PSS) Slog.d(TAG_PSS,
- "Showing dump heap notification from " + procName + "/" + uid);
-
- Intent dumpFinishedIntent = new Intent(ACTION_HEAP_DUMP_FINISHED);
- // Send this only to the Shell package.
- dumpFinishedIntent.setPackage("com.android.shell");
- dumpFinishedIntent.putExtra(Intent.EXTRA_UID, uid);
- dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_IS_USER_INITIATED, isUserInitiated);
- dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_SIZE_BYTES, memLimit);
- dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_REPORT_PACKAGE, reportPackage);
- dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_PROCESS_NAME, procName);
-
- mContext.sendBroadcastAsUser(dumpFinishedIntent,
- UserHandle.getUserHandleForUid(uid));
+ mAppProfiler.handlePostDumpHeapNotification();
} break;
case ABORT_DUMPHEAP_MSG: {
- String procName = (String) msg.obj;
- if (procName != null) {
- synchronized (ActivityManagerService.this) {
- if (procName.equals(mMemWatchDumpProcName)) {
- mMemWatchDumpProcName = null;
- mMemWatchDumpUri = null;
- mMemWatchDumpPid = -1;
- mMemWatchDumpUid = -1;
- }
- }
- }
+ mAppProfiler.handleAbortDumpHeap((String) msg.obj);
} break;
case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
@@ -1841,147 +1586,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- static final int COLLECT_PSS_BG_MSG = 1;
- static final int DEFER_PSS_MSG = 2;
- static final int STOP_DEFERRING_PSS_MSG = 3;
-
- final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case COLLECT_PSS_BG_MSG: {
- long start = SystemClock.uptimeMillis();
- MemInfoReader memInfo = null;
- synchronized (ActivityManagerService.this) {
- if (mFullPssPending) {
- mFullPssPending = false;
- memInfo = new MemInfoReader();
- }
- }
- if (memInfo != null) {
- updateCpuStatsNow();
- long nativeTotalPss = 0;
- final List<ProcessCpuTracker.Stats> stats;
- synchronized (mProcessCpuTracker) {
- stats = mProcessCpuTracker.getStats( (st)-> {
- return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
- });
- }
- final int N = stats.size();
- for (int j = 0; j < N; j++) {
- synchronized (mPidsSelfLocked) {
- if (mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
- // This is one of our own processes; skip it.
- continue;
- }
- }
- nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
- }
- memInfo.readMemInfo();
- synchronized (mProcessStats.mLock) {
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in "
- + (SystemClock.uptimeMillis()-start) + "ms");
- final long cachedKb = memInfo.getCachedSizeKb();
- final long freeKb = memInfo.getFreeSizeKb();
- final long zramKb = memInfo.getZramTotalSizeKb();
- final long kernelKb = memInfo.getKernelUsedSizeKb();
- EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
- kernelKb*1024, nativeTotalPss*1024);
- mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
- nativeTotalPss);
- }
- }
-
- int num = 0;
- long[] tmp = new long[3];
- do {
- ProcessRecord proc;
- int procState;
- int statType;
- int pid = -1;
- long lastPssTime;
- synchronized (ActivityManagerService.this) {
- if (mPendingPssProcesses.size() <= 0) {
- if (mTestPssMode || DEBUG_PSS) Slog.d(TAG_PSS,
- "Collected pss of " + num + " processes in "
- + (SystemClock.uptimeMillis() - start) + "ms");
- mPendingPssProcesses.clear();
- return;
- }
- proc = mPendingPssProcesses.remove(0);
- procState = proc.pssProcState;
- statType = proc.pssStatType;
- lastPssTime = proc.lastPssTime;
- long now = SystemClock.uptimeMillis();
- if (proc.thread != null && procState == proc.setProcState
- && (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
- < now) {
- pid = proc.pid;
- } else {
- ProcessList.abortNextPssTime(proc.procStateMemTracker);
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
- ": still need " +
- (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE-now) +
- "ms until safe");
- proc = null;
- pid = 0;
- }
- }
- if (proc != null) {
- long startTime = SystemClock.currentThreadTimeMillis();
- // skip background PSS calculation of apps that are capturing
- // camera imagery
- final boolean usingCamera = isCameraActiveForUid(proc.uid);
- long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
- long endTime = SystemClock.currentThreadTimeMillis();
- synchronized (ActivityManagerService.this) {
- if (pss != 0 && proc.thread != null && proc.setProcState == procState
- && proc.pid == pid && proc.lastPssTime == lastPssTime) {
- num++;
- ProcessList.commitNextPssTime(proc.procStateMemTracker);
- recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
- statType, endTime-startTime, SystemClock.uptimeMillis());
- } else {
- ProcessList.abortNextPssTime(proc.procStateMemTracker);
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
- ": " + (proc.thread == null ? "NO_THREAD " : "") +
- (usingCamera ? "CAMERA " : "") +
- (proc.pid != pid ? "PID_CHANGED " : "") +
- " initState=" + procState + " curState=" +
- proc.setProcState + " " +
- (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
- }
- }
- }
- } while (true);
- }
-
- case DEFER_PSS_MSG: {
- deferPssForActivityStart();
- } break;
-
- case STOP_DEFERRING_PSS_MSG: {
- final int nesting = mActivityStartingNesting.decrementAndGet();
- if (nesting <= 0) {
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
- + nesting);
- }
- if (nesting < 0) {
- Slog.wtf(TAG, "Activity start nesting undercount!");
- mActivityStartingNesting.incrementAndGet();
- }
- } else {
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
- }
- }
- }
- break;
-
- }
- }
- };
public void setSystemProcess() {
try {
@@ -1992,10 +1596,7 @@ public class ActivityManagerService extends IActivityManager.Stub
DUMP_FLAG_PRIORITY_HIGH);
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
- if (MONITOR_CPU_USAGE) {
- ServiceManager.addService("cpuinfo", new CpuBinder(this),
- /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
- }
+ mAppProfiler.setCpuInfoService();
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));
ServiceManager.addService("cacheinfo", new CacheBinder(this));
@@ -2152,37 +1753,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- static class CpuBinder extends Binder {
- ActivityManagerService mActivityManagerService;
- private final PriorityDump.PriorityDumper mPriorityDumper =
- new PriorityDump.PriorityDumper() {
- @Override
- public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
- boolean asProto) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
- "cpuinfo", pw)) return;
- synchronized (mActivityManagerService.mProcessCpuTracker) {
- if (asProto) {
- mActivityManagerService.mProcessCpuTracker.dumpProto(fd);
- return;
- }
- pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
- pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
- SystemClock.uptimeMillis()));
- }
- }
- };
-
- CpuBinder(ActivityManagerService activityManagerService) {
- mActivityManagerService = activityManagerService;
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- PriorityDump.dump(mPriorityDumper, fd, pw, args);
- }
- }
-
static class CacheBinder extends Binder {
ActivityManagerService mActivityManagerService;
@@ -2425,13 +1995,12 @@ public class ActivityManagerService extends IActivityManager.Stub
mPlatformCompat = null;
mProcessList = injector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
- mLowMemDetector = null;
+ mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null);
mOomAdjuster = hasHandlerThread
? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
mIntentFirewall = hasHandlerThread
? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
- mProcessCpuThread = null;
mProcessStats = null;
mCpHelper = new ContentProviderHelper(this, false);
// For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
@@ -2482,7 +2051,8 @@ public class ActivityManagerService extends IActivityManager.Stub
Context.PLATFORM_COMPAT_SERVICE);
mProcessList = mInjector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
- mLowMemDetector = new LowMemDetector(this);
+ mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(),
+ new LowMemDetector(this));
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
// Broadcast policy parameters
@@ -2554,40 +2124,6 @@ public class ActivityManagerService extends IActivityManager.Stub
DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
- mProcessCpuThread = new Thread("CpuTracker") {
- @Override
- public void run() {
- synchronized (mProcessCpuTracker) {
- mProcessCpuInitLatch.countDown();
- mProcessCpuTracker.init();
- }
- while (true) {
- try {
- try {
- synchronized(this) {
- final long now = SystemClock.uptimeMillis();
- long nextCpuDelay = (mLastCpuTime.get()+MONITOR_CPU_MAX_TIME)-now;
- long nextWriteDelay = (mLastWriteTime+BATTERY_STATS_TIME)-now;
- //Slog.i(TAG, "Cpu delay=" + nextCpuDelay
- // + ", write delay=" + nextWriteDelay);
- if (nextWriteDelay < nextCpuDelay) {
- nextCpuDelay = nextWriteDelay;
- }
- if (nextCpuDelay > 0) {
- mProcessCpuMutexFree.set(true);
- this.wait(nextCpuDelay);
- }
- }
- } catch (InterruptedException e) {
- }
- updateCpuStatsNow();
- } catch (Exception e) {
- Slog.e(TAG, "Unexpected exception collecting process stats", e);
- }
- }
- }
- };
-
mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
Watchdog.getInstance().addMonitor(this);
@@ -2621,7 +2157,6 @@ public class ActivityManagerService extends IActivityManager.Stub
private void start() {
removeAllProcessGroups();
- mProcessCpuThread.start();
mBatteryStatsService.publish();
mAppOpsService.publish();
@@ -2629,16 +2164,7 @@ public class ActivityManagerService extends IActivityManager.Stub
LocalServices.addService(ActivityManagerInternal.class, mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
mPendingIntentController.onActivityManagerInternalAdded();
- // Wait for the synchronized block started in mProcessCpuThread,
- // so that any other access to mProcessCpuTracker from main thread
- // will be blocked during mProcessCpuTracker initialization.
- try {
- mProcessCpuInitLatch.await();
- } catch (InterruptedException e) {
- Slog.wtf(TAG, "Interrupted wait during start", e);
- Thread.currentThread().interrupt();
- throw new IllegalStateException("Interrupted wait during start");
- }
+ mAppProfiler.onActivityManagerInternalAdded();
}
public void initPowerManagement() {
@@ -2781,116 +2307,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- void updateCpuStats() {
- final long now = SystemClock.uptimeMillis();
- if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
- return;
- }
- if (mProcessCpuMutexFree.compareAndSet(true, false)) {
- synchronized (mProcessCpuThread) {
- mProcessCpuThread.notify();
- }
- }
+ void updateCpuStatsLocked() {
+ mAppProfiler.updateCpuStatsLocked();
}
void updateCpuStatsNow() {
- synchronized (mProcessCpuTracker) {
- mProcessCpuMutexFree.set(false);
- final long now = SystemClock.uptimeMillis();
- boolean haveNewCpuStats = false;
-
- if (MONITOR_CPU_USAGE &&
- mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) {
- mLastCpuTime.set(now);
- mProcessCpuTracker.update();
- if (mProcessCpuTracker.hasGoodLastStats()) {
- haveNewCpuStats = true;
- //Slog.i(TAG, mProcessCpu.printCurrentState());
- //Slog.i(TAG, "Total CPU usage: "
- // + mProcessCpu.getTotalCpuPercent() + "%");
-
- // Slog the cpu usage if the property is set.
- if ("true".equals(SystemProperties.get("events.cpu"))) {
- int user = mProcessCpuTracker.getLastUserTime();
- int system = mProcessCpuTracker.getLastSystemTime();
- int iowait = mProcessCpuTracker.getLastIoWaitTime();
- int irq = mProcessCpuTracker.getLastIrqTime();
- int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
- int idle = mProcessCpuTracker.getLastIdleTime();
-
- int total = user + system + iowait + irq + softIrq + idle;
- if (total == 0) total = 1;
-
- EventLogTags.writeCpu(
- ((user + system + iowait + irq + softIrq) * 100) / total,
- (user * 100) / total,
- (system * 100) / total,
- (iowait * 100) / total,
- (irq * 100) / total,
- (softIrq * 100) / total);
- }
- }
- }
-
- final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
- synchronized (bstats) {
- if (haveNewCpuStats) {
- if (bstats.startAddingCpuLocked()) {
- int totalUTime = 0;
- int totalSTime = 0;
- final int statsCount = mProcessCpuTracker.countStats();
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long uptime = SystemClock.uptimeMillis();
- synchronized (mPidsSelfLocked) {
- for (int i = 0; i < statsCount; i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- if (!st.working) {
- continue;
- }
- ProcessRecord pr = mPidsSelfLocked.get(st.pid);
- totalUTime += st.rel_utime;
- totalSTime += st.rel_stime;
- if (pr != null) {
- BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
- if (ps == null || !ps.isActive()) {
- pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
- pr.info.uid, pr.processName,
- elapsedRealtime, uptime);
- }
- ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
- pr.curCpuTime += st.rel_utime + st.rel_stime;
- if (pr.lastCpuTime == 0) {
- pr.lastCpuTime = pr.curCpuTime;
- }
- } else {
- BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
- if (ps == null || !ps.isActive()) {
- st.batteryStats = ps = bstats.getProcessStatsLocked(
- bstats.mapUid(st.uid), st.name,
- elapsedRealtime, uptime);
- }
- ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
- }
- }
- }
-
- final int userTime = mProcessCpuTracker.getLastUserTime();
- final int systemTime = mProcessCpuTracker.getLastSystemTime();
- final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime();
- final int irqTime = mProcessCpuTracker.getLastIrqTime();
- final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
- final int idleTime = mProcessCpuTracker.getLastIdleTime();
- bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
- systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
- }
- }
-
- if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
- mLastWriteTime = now;
- mBatteryStatsService.scheduleWriteToDisk();
- }
- }
- }
+ mAppProfiler.updateCpuStatsNow();
}
@Override
@@ -3510,9 +2932,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- if (mProfileData.getProfileProc() == app) {
- clearProfilerLocked();
- }
+ mAppProfiler.onAppDiedLocked(app);
mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
Slog.w(TAG, "Crash of app " + app.processName
@@ -3556,7 +2976,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// and the app that died was not running instrumentation,
// then tell everyone we are now low on memory.
if (!mProcessList.haveBackgroundProcessLocked()) {
- boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+ boolean doReport = Build.IS_DEBUGGABLE;
if (doReport) {
long now = SystemClock.uptimeMillis();
if (now < (mLastMemUsageReportTime+5*60*1000)) {
@@ -3641,11 +3061,11 @@ public class ActivityManagerService extends IActivityManager.Stub
"Process " + app.processName + " (pid " + pid + ") has died: "
+ ProcessList.makeOomAdjString(app.setAdj, true) + " "
+ ProcessList.makeProcStateString(app.setProcState), app.info.uid);
- mAllowLowerMemLevel = true;
+ mAppProfiler.setAllowLowerMemLevelLocked(true);
} else {
// Note that we always want to do oom adj to update our state with the
// new number of procs.
- mAllowLowerMemLevel = false;
+ mAppProfiler.setAllowLowerMemLevelLocked(false);
doLowMem = false;
}
EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
@@ -4141,7 +3561,7 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
// Allow memory level to go down (the flag needs to be set before updating oom adj)
// because this method is also used to simulate low memory.
- mAllowLowerMemLevel = true;
+ mAppProfiler.setAllowLowerMemLevelLocked(true);
mProcessList.killPackageProcessesLocked(null /* packageName */, -1 /* appId */,
UserHandle.USER_ALL, ProcessList.CACHED_APP_MIN_ADJ,
ApplicationExitInfo.REASON_USER_REQUESTED,
@@ -4926,48 +4346,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackage(appInfo);
- ProfilerInfo profilerInfo = null;
- String preBindAgent = null;
- if (mProfileData.getProfileApp() != null
- && mProfileData.getProfileApp().equals(processName)) {
- mProfileData.setProfileProc(app);
- if (mProfileData.getProfilerInfo() != null) {
- // Send a profiler info object to the app if either a file is given, or
- // an agent should be loaded at bind-time.
- boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
- || mProfileData.getProfilerInfo().attachAgentDuringBind;
- profilerInfo = needsInfo
- ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
- if (mProfileData.getProfilerInfo().agent != null) {
- preBindAgent = mProfileData.getProfilerInfo().agent;
- }
- }
- } else if (instr != null && instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
- null, false);
- }
- if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
- // We need to do a debuggable check here. See setAgentApp for why the check is
- // postponed to here.
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- String agent = mAppAgentMap.get(processName);
- // Do not overwrite already requested agent.
- if (profilerInfo == null) {
- profilerInfo = new ProfilerInfo(null, null, 0, false, false,
- mAppAgentMap.get(processName), true);
- } else if (profilerInfo.agent == null) {
- profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
- }
- }
- }
-
- if (profilerInfo != null && profilerInfo.profileFd != null) {
- profilerInfo.profileFd = profilerInfo.profileFd.dup();
- if (TextUtils.equals(mProfileData.getProfileApp(), processName)
- && mProfileData.getProfilerInfo() != null) {
- clearProfilerLocked();
- }
- }
+ ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
// We deprecated Build.SERIAL and it is not accessible to
// Instant Apps and target APIs higher than O MR1. Since access to the serial
@@ -4976,44 +4355,6 @@ public class ActivityManagerService extends IActivityManager.Stub
&& appInfo.targetSdkVersion < Build.VERSION_CODES.P)
? sTheRealBuildSerial : Build.UNKNOWN;
- // Check if this is a secondary process that should be incorporated into some
- // currently active instrumentation. (Note we do this AFTER all of the profiling
- // stuff above because profiling can currently happen only in the primary
- // instrumentation process.)
- if (mActiveInstrumentation.size() > 0 && instr == null) {
- for (int i = mActiveInstrumentation.size() - 1;
- i >= 0 && app.getActiveInstrumentation() == null; i--) {
- ActiveInstrumentation aInstr = mActiveInstrumentation.get(i);
- if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
- if (aInstr.mTargetProcesses.length == 0) {
- // This is the wildcard mode, where every process brought up for
- // the target instrumentation should be included.
- if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
- app.setActiveInstrumentation(aInstr);
- aInstr.mRunningProcesses.add(app);
- }
- } else {
- for (String proc : aInstr.mTargetProcesses) {
- if (proc.equals(app.processName)) {
- app.setActiveInstrumentation(aInstr);
- aInstr.mRunningProcesses.add(app);
- break;
- }
- }
- }
- }
- }
- }
-
- // If we were asked to attach an agent on startup, do so now, before we're binding
- // application code.
- if (preBindAgent != null) {
- thread.attachAgent(preBindAgent);
- }
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- thread.attachStartupAgents(app.info.dataDir);
- }
-
// Figure out whether the app needs to run in autofill compat mode.
AutofillOptions autofillOptions = null;
if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) {
@@ -5302,7 +4643,8 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
- requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+ mAppProfiler.requestPssAllProcsLocked(
+ SystemClock.uptimeMillis(), true, false);
}
}
});
@@ -6902,32 +6244,13 @@ public class ActivityManagerService extends IActivityManager.Stub
"Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- if (agent == null) {
- if (mAppAgentMap != null) {
- mAppAgentMap.remove(packageName);
- if (mAppAgentMap.isEmpty()) {
- mAppAgentMap = null;
- }
- }
- } else {
- if (mAppAgentMap == null) {
- mAppAgentMap = new HashMap<>();
- }
- if (mAppAgentMap.size() >= 100) {
- // Limit the size of the map, to avoid OOMEs.
- Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
- + "/" + agent);
- return;
- }
- mAppAgentMap.put(packageName, agent);
- }
+ mAppProfiler.setAgentAppLocked(packageName, agent);
}
}
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
+ if (!Build.IS_DEBUGGABLE) {
if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + app.packageName);
}
@@ -6939,8 +6262,7 @@ public class ActivityManagerService extends IActivityManager.Stub
void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo) {
synchronized (this) {
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
+ if (!Build.IS_DEBUGGABLE) {
boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean isAppProfileable = app.isProfileableByShell();
if (!isAppDebuggable && !isAppProfileable) {
@@ -6948,24 +6270,12 @@ public class ActivityManagerService extends IActivityManager.Stub
+ "and not profileable by shell: " + app.packageName);
}
}
- mProfileData.setProfileApp(processName);
-
- if (mProfileData.getProfilerInfo() != null) {
- if (mProfileData.getProfilerInfo().profileFd != null) {
- try {
- mProfileData.getProfilerInfo().profileFd.close();
- } catch (IOException e) {
- }
- }
- }
- mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
- mProfileType = 0;
+ mAppProfiler.setProfileAppLocked(processName, profilerInfo);
}
}
void setNativeDebuggingAppLocked(ApplicationInfo app, String processName) {
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
+ if (!Build.IS_DEBUGGABLE) {
if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + app.packageName);
}
@@ -7047,7 +6357,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return;
}
synchronized (this) {
- startHeapDumpLocked(pr, true);
+ mAppProfiler.startHeapDumpLocked(pr, true);
}
}
@@ -7823,10 +7133,6 @@ public class ActivityManagerService extends IActivityManager.Stub
br.onReceive(mContext, intent);
}
- private long getLowRamTimeSinceIdle(long now) {
- return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now-mLowRamStartTime) : 0);
- }
-
@Override
public void performIdleMaintenance() {
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
@@ -7842,12 +7148,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// Compact all non-zygote processes to freshen up the page cache.
mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
- final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now);
+ final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLocked(now);
mLastIdleTime = now;
- mLowRamTimeSinceLastIdle = 0;
- if (mLowRamStartTime != 0) {
- mLowRamStartTime = now;
- }
+ mAppProfiler.updateLowRamTimestampLocked(now);
StringBuilder sb = new StringBuilder(128);
sb.append("Idle maintenance over ");
@@ -7898,8 +7201,7 @@ public class ActivityManagerService extends IActivityManager.Stub
&& proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
proc.notCachedSinceIdle = true;
proc.initialIdlePss = 0;
- proc.nextPssTime = ProcessList.computeNextPssTime(proc.setProcState, null,
- mTestPssMode, mAtmInternal.isSleeping(), now);
+ mAppProfiler.updateNextPssTimeLocked(proc.setProcState, proc, now, true);
}
}
}
@@ -7937,11 +7239,7 @@ public class ActivityManagerService extends IActivityManager.Stub
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
mHiddenApiBlacklist.registerObserver();
- final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- ActivityThread.currentApplication().getMainExecutor(),
- mPssDelayConfigListener);
+ mAppProfiler.retrieveSettings();
synchronized (this) {
mDebugApp = mOrigDebugApp = debugApp;
@@ -7960,7 +7258,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers,
delayUserDataLocking);
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
- mPssDeferralTime = pssDeferralMs;
}
}
@@ -8950,7 +8247,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public int getMemoryTrimLevel() {
enforceNotIsolatedCaller("getMyMemoryState");
synchronized (this) {
- return mLastMemoryLevel;
+ return mAppProfiler.getLastMemoryLevelLocked();
}
}
@@ -9961,7 +9258,7 @@ public class ActivityManagerService extends IActivityManager.Stub
needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
- mTestPssMode, mWakefulness);
+ mAppProfiler.getTestPssModeLocked(), mWakefulness);
if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
if (needSep) pw.println();
@@ -10005,35 +9302,7 @@ public class ActivityManagerService extends IActivityManager.Stub
+ " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
}
}
- if (mMemWatchProcesses.getMap().size() > 0) {
- pw.println(" Mem watch processes:");
- final ArrayMap<String, SparseArray<Pair<Long, String>>> procs
- = mMemWatchProcesses.getMap();
- for (int i=0; i<procs.size(); i++) {
- final String proc = procs.keyAt(i);
- final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
- for (int j=0; j<uids.size(); j++) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- StringBuilder sb = new StringBuilder();
- sb.append(" ").append(proc).append('/');
- UserHandle.formatUid(sb, uids.keyAt(j));
- Pair<Long, String> val = uids.valueAt(j);
- sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
- if (val.second != null) {
- sb.append(", report to ").append(val.second);
- }
- pw.println(sb.toString());
- }
- }
- pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
- pw.print(" mMemWatchDumpUri="); pw.println(mMemWatchDumpUri);
- pw.print(" mMemWatchDumpPid="); pw.println(mMemWatchDumpPid);
- pw.print(" mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
- pw.print(" mMemWatchIsUserInitiated="); pw.println(mMemWatchIsUserInitiated);
- }
+ needSep = mAppProfiler.dumpMemWatchProcessesLocked(pw, needSep);
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
if (needSep) {
@@ -10043,29 +9312,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println(" mTrackAllocationApp=" + mTrackAllocationApp);
}
}
- if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
- || (mProfileData.getProfilerInfo() != null &&
- (mProfileData.getProfilerInfo().profileFile != null
- || mProfileData.getProfilerInfo().profileFd != null))) {
- if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- pw.println(" mProfileApp=" + mProfileData.getProfileApp()
- + " mProfileProc=" + mProfileData.getProfileProc());
- if (mProfileData.getProfilerInfo() != null) {
- pw.println(" mProfileFile=" + mProfileData.getProfilerInfo().profileFile
- + " mProfileFd=" + mProfileData.getProfilerInfo().profileFd);
- pw.println(" mSamplingInterval="
- + mProfileData.getProfilerInfo().samplingInterval +
- " mAutoStopProfiler="
- + mProfileData.getProfilerInfo().autoStopProfiler +
- " mStreamingOutput=" + mProfileData.getProfilerInfo().streamingOutput);
- pw.println(" mProfileType=" + mProfileType);
- }
- }
- }
+ needSep = mAppProfiler.dumpProfileDataLocked(pw, dumpPackage, needSep);
if (mNativeDebuggingApp != null) {
if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
if (needSep) {
@@ -10093,14 +9340,13 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println("");
mOomAdjuster.dumpSequenceNumbersLocked(pw);
mOomAdjuster.dumpProcCountsLocked(pw);
- pw.println(" mAllowLowerMemLevel=" + mAllowLowerMemLevel
- + " mLastMemoryLevel=" + mLastMemoryLevel
- + " mLastNumProcesses=" + mLastNumProcesses);
+ mAppProfiler.dumpMemoryLevelsLocked(pw);
long now = SystemClock.uptimeMillis();
pw.print(" mLastIdleTime=");
TimeUtils.formatDuration(now, mLastIdleTime, pw);
pw.print(" mLowRamSinceLastIdle=");
- TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw);
+ TimeUtils.formatDuration(
+ mAppProfiler.getLowRamTimeSinceIdleLocked(now), pw);
pw.println();
pw.println();
@@ -10248,7 +9494,8 @@ public class ActivityManagerService extends IActivityManager.Stub
dumpPackage);
mAppErrors.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
dumpPackage);
- mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode);
+ mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness,
+ mAppProfiler.getTestPssModeLocked());
if (dumpPackage == null) {
mUserController.dumpDebug(proto,
@@ -10285,42 +9532,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- if (mMemWatchProcesses.getMap().size() > 0) {
- final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
- ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
- for (int i=0; i<procs.size(); i++) {
- final String proc = procs.keyAt(i);
- final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
- final long ptoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME, proc);
- for (int j=0; j<uids.size(); j++) {
- final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MEM_STATS);
- Pair<Long, String> val = uids.valueAt(j);
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
- DebugUtils.sizeValueToString(val.first, new StringBuilder()));
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
- proto.end(utoken);
- }
- proto.end(ptoken);
- }
-
- final long dtoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME,
- mMemWatchDumpProcName);
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.URI,
- mMemWatchDumpUri.toString());
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID,
- mMemWatchDumpPid);
- proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID,
- mMemWatchDumpUid);
- proto.write(
- ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.IS_USER_INITIATED,
- mMemWatchIsUserInitiated);
- proto.end(dtoken);
-
- proto.end(token);
- }
+ mAppProfiler.writeMemWatchProcessToProtoLocked(proto);
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
@@ -10329,25 +9541,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
- || (mProfileData.getProfilerInfo() != null &&
- (mProfileData.getProfilerInfo().profileFile != null
- || mProfileData.getProfilerInfo().profileFd != null))) {
- if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
- final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
- proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME,
- mProfileData.getProfileApp());
- mProfileData.getProfileProc().dumpDebug(proto,
- ActivityManagerServiceDumpProcessesProto.Profile.PROC);
- if (mProfileData.getProfilerInfo() != null) {
- mProfileData.getProfilerInfo().dumpDebug(proto,
- ActivityManagerServiceDumpProcessesProto.Profile.INFO);
- proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE,
- mProfileType);
- }
- proto.end(token);
- }
- }
+ mAppProfiler.writeProfileDataToProtoLocked(proto, dumpPackage);
if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
proto.write(ActivityManagerServiceDumpProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
@@ -10365,12 +9559,11 @@ public class ActivityManagerService extends IActivityManager.Stub
proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
mOomAdjuster.dumpProcessListVariablesLocked(proto);
- proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
- proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
- proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+ mAppProfiler.writeMemoryLevelsToProtoLocked(proto);
long now = SystemClock.uptimeMillis();
ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
- proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
+ proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS,
+ mAppProfiler.getLowRamTimeSinceIdleLocked(now));
}
}
@@ -11553,7 +10746,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
- MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
+ MemoryUsageDumpOptions opts, final String[] innerArgs, boolean brief,
ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
@@ -11573,16 +10766,13 @@ public class ActivityManagerService extends IActivityManager.Stub
findPid = Integer.parseInt(innerArgs[0]);
} catch (NumberFormatException e) {
}
- synchronized (mProcessCpuTracker) {
- final int N = mProcessCpuTracker.countStats();
- for (int i=0; i<N; i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- if (st.pid == findPid || (st.baseName != null
- && st.baseName.equals(innerArgs[0]))) {
- nativeProcs.add(st);
- }
+ final int fFindPid = findPid;
+ mAppProfiler.forAllCpuStats((st) -> {
+ if (st.pid == fFindPid || (st.baseName != null
+ && st.baseName.equals(innerArgs[0]))) {
+ nativeProcs.add(st);
}
- }
+ });
if (nativeProcs.size() > 0) {
dumpApplicationMemoryUsageHeader(pw, uptime, realtime,
opts.isCheckinRequest, opts.isCompact);
@@ -11640,21 +10830,13 @@ public class ActivityManagerService extends IActivityManager.Stub
ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
- long nativePss = 0;
- long nativeSwapPss = 0;
- long nativeRss = 0;
- long dalvikPss = 0;
- long dalvikSwapPss = 0;
- long dalvikRss = 0;
+ final long[] ss = new long[INDEX_LAST];
long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
long[] dalvikSubitemRss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
- long otherPss = 0;
- long otherSwapPss = 0;
- long otherRss = 0;
long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
@@ -11665,7 +10847,6 @@ public class ActivityManagerService extends IActivityManager.Stub
ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
new ArrayList[DUMP_MEM_OOM_LABEL.length];
- long totalPss = 0;
long totalSwapPss = 0;
long totalRss = 0;
long cachedPss = 0;
@@ -11774,40 +10955,40 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (!opts.isCheckinRequest && mi != null) {
- totalPss += myTotalPss;
- totalSwapPss += myTotalSwapPss;
- totalRss += myTotalRss;
+ ss[INDEX_TOTAL_PSS] += myTotalPss;
+ ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+ ss[INDEX_TOTAL_RSS] += myTotalRss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
myTotalSwapPss, myTotalRss, pid, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
- nativePss += mi.nativePss;
- nativeSwapPss += mi.nativeSwappedOutPss;
- nativeRss += mi.nativeRss;
- dalvikPss += mi.dalvikPss;
- dalvikSwapPss += mi.dalvikSwappedOutPss;
- dalvikRss += mi.dalvikRss;
+ ss[INDEX_NATIVE_PSS] += mi.nativePss;
+ ss[INDEX_NATIVE_SWAP_PSS] += mi.nativeSwappedOutPss;
+ ss[INDEX_NATIVE_RSS] += mi.nativeRss;
+ ss[INDEX_DALVIK_PSS] += mi.dalvikPss;
+ ss[INDEX_DALVIK_SWAP_PSS] += mi.dalvikSwappedOutPss;
+ ss[INDEX_DALVIK_RSS] += mi.dalvikRss;
for (int j=0; j<dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemSwapPss[j] +=
mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
- otherPss += mi.otherPss;
- otherRss += mi.otherRss;
- otherSwapPss += mi.otherSwappedOutPss;
+ ss[INDEX_OTHER_PSS] += mi.otherPss;
+ ss[INDEX_OTHER_RSS] += mi.otherRss;
+ ss[INDEX_OTHER_SWAP_PSS] += mi.otherSwappedOutPss;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
- otherPss -= mem;
+ ss[INDEX_OTHER_PSS] -= mem;
mem = mi.getOtherSwappedOutPss(j);
miscSwapPss[j] += mem;
- otherSwapPss -= mem;
+ ss[INDEX_OTHER_SWAP_PSS] -= mem;
mem = mi.getOtherRss(j);
miscRss[j] += mem;
- otherRss -= mem;
+ ss[INDEX_OTHER_RSS] -= mem;
}
if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
@@ -11837,86 +11018,87 @@ public class ActivityManagerService extends IActivityManager.Stub
if (collectNative) {
mi = null;
- synchronized (mProcessCpuTracker) {
- final int N = mProcessCpuTracker.countStats();
- for (int i=0; i<N; i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
- if (mi == null) {
- mi = new Debug.MemoryInfo();
+ final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
+ mAppProfiler.forAllCpuStats((st) -> {
+ if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+ if (memInfos[0] == null) {
+ memInfos[0] = new Debug.MemoryInfo();
+ }
+ final Debug.MemoryInfo info = memInfos[0];
+ if (!brief && !opts.oomOnly) {
+ if (!Debug.getMemoryInfo(st.pid, info)) {
+ return;
}
- if (!brief && !opts.oomOnly) {
- if (!Debug.getMemoryInfo(st.pid, mi)) {
- continue;
- }
- } else {
- long pss = Debug.getPss(st.pid, tmpLong, null);
- if (pss == 0) {
- continue;
- }
- mi.nativePss = (int) pss;
- mi.nativePrivateDirty = (int) tmpLong[0];
- mi.nativeRss = (int) tmpLong[2];
+ } else {
+ long pss = Debug.getPss(st.pid, tmpLong, null);
+ if (pss == 0) {
+ return;
}
+ info.nativePss = (int) pss;
+ info.nativePrivateDirty = (int) tmpLong[0];
+ info.nativeRss = (int) tmpLong[2];
+ }
- final long myTotalPss = mi.getTotalPss();
- final long myTotalSwapPss = mi.getTotalSwappedOutPss();
- final long myTotalRss = mi.getTotalRss();
- totalPss += myTotalPss;
- totalSwapPss += myTotalSwapPss;
- totalRss += myTotalRss;
- nativeProcTotalPss += myTotalPss;
-
- MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
- st.name, myTotalPss, mi.getSummaryTotalSwapPss(), myTotalRss,
- st.pid, false);
- procMems.add(pssItem);
-
- nativePss += mi.nativePss;
- nativeSwapPss += mi.nativeSwappedOutPss;
- nativeRss += mi.nativeRss;
- dalvikPss += mi.dalvikPss;
- dalvikSwapPss += mi.dalvikSwappedOutPss;
- dalvikRss += mi.dalvikRss;
- for (int j=0; j<dalvikSubitemPss.length; j++) {
- dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
- dalvikSubitemSwapPss[j] +=
- mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
- dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
- + j);
- }
- otherPss += mi.otherPss;
- otherSwapPss += mi.otherSwappedOutPss;
- otherRss += mi.otherRss;
- for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
- long mem = mi.getOtherPss(j);
- miscPss[j] += mem;
- otherPss -= mem;
- mem = mi.getOtherSwappedOutPss(j);
- miscSwapPss[j] += mem;
- otherSwapPss -= mem;
- mem = mi.getOtherRss(j);
- miscRss[j] += mem;
- otherRss -= mem;
- }
- oomPss[0] += myTotalPss;
- oomSwapPss[0] += myTotalSwapPss;
- if (oomProcs[0] == null) {
- oomProcs[0] = new ArrayList<MemItem>();
- }
- oomProcs[0].add(pssItem);
- oomRss[0] += myTotalRss;
+ final long myTotalPss = info.getTotalPss();
+ final long myTotalSwapPss = info.getTotalSwappedOutPss();
+ final long myTotalRss = info.getTotalRss();
+ ss[INDEX_TOTAL_PSS] += myTotalPss;
+ ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+ ss[INDEX_TOTAL_RSS] += myTotalRss;
+ ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
+
+ MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
+ st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
+ st.pid, false);
+ procMems.add(pssItem);
+
+ ss[INDEX_NATIVE_PSS] += info.nativePss;
+ ss[INDEX_NATIVE_SWAP_PSS] += info.nativeSwappedOutPss;
+ ss[INDEX_NATIVE_RSS] += info.nativeRss;
+ ss[INDEX_DALVIK_PSS] += info.dalvikPss;
+ ss[INDEX_DALVIK_SWAP_PSS] += info.dalvikSwappedOutPss;
+ ss[INDEX_DALVIK_RSS] += info.dalvikRss;
+ for (int j = 0; j < dalvikSubitemPss.length; j++) {
+ dalvikSubitemPss[j] += info.getOtherPss(
+ Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemSwapPss[j] +=
+ info.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemRss[j] += info.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
+ + j);
}
+ ss[INDEX_OTHER_PSS] += info.otherPss;
+ ss[INDEX_OTHER_SWAP_PSS] += info.otherSwappedOutPss;
+ ss[INDEX_OTHER_RSS] += info.otherRss;
+ for (int j = 0; j < Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+ long mem = info.getOtherPss(j);
+ miscPss[j] += mem;
+ ss[INDEX_OTHER_PSS] -= mem;
+ mem = info.getOtherSwappedOutPss(j);
+ miscSwapPss[j] += mem;
+ ss[INDEX_OTHER_SWAP_PSS] -= mem;
+ mem = info.getOtherRss(j);
+ miscRss[j] += mem;
+ ss[INDEX_OTHER_RSS] -= mem;
+ }
+ oomPss[0] += myTotalPss;
+ oomSwapPss[0] += myTotalSwapPss;
+ if (oomProcs[0] == null) {
+ oomProcs[0] = new ArrayList<MemItem>();
+ }
+ oomProcs[0].add(pssItem);
+ oomRss[0] += myTotalRss;
}
- }
+ });
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
- catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, nativeRss, -1));
+ catMems.add(new MemItem("Native", "Native",
+ ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
final int dalvikId = -2;
- catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikRss,
- dalvikId));
- catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, otherRss, -3));
+ catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS],
+ ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId));
+ catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS],
+ ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3));
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
String label = Debug.MemoryInfo.getOtherLabel(j);
catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j));
@@ -11982,7 +11164,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, false, false);
}
- opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
+ opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && ss[INDEX_TOTAL_SWAP_PSS] != 0;
if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total PSS by process:");
@@ -12008,40 +11190,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
- if (nativeProcTotalPss > 0) {
+ if (ss[INDEX_TOTAL_NATIVE_PSS] > 0) {
synchronized (mProcessStats.mLock) {
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
final long kernelKb = memInfo.getKernelUsedSizeKb();
- EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
- kernelKb*1024, nativeProcTotalPss*1024);
+ EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+ kernelKb * 1024, ss[INDEX_TOTAL_NATIVE_PSS] * 1024);
mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
- nativeProcTotalPss);
+ ss[INDEX_TOTAL_NATIVE_PSS]);
}
}
if (!brief) {
if (!opts.isCompact) {
pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
pw.print(" (status ");
- switch (mLastMemoryLevel) {
- case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
- pw.println("normal)");
- break;
- case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- pw.println("moderate)");
- break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
- pw.println("low)");
- break;
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- pw.println("critical)");
- break;
- default:
- pw.print(mLastMemoryLevel);
- pw.println(")");
- break;
- }
+ mAppProfiler.dumpLastMemoryLevelLocked(pw);
pw.print(" Free RAM: ");
pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()));
@@ -12056,7 +11221,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(",");
pw.print(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()); pw.print(",");
- pw.println(totalPss - cachedPss);
+ pw.println(ss[INDEX_TOTAL_PSS] - cachedPss);
}
}
long kernelUsed = memInfo.getKernelUsedSizeKb();
@@ -12078,13 +11243,14 @@ public class ActivityManagerService extends IActivityManager.Stub
// set on ION VMAs, therefore consider the entire ION heap as used kernel memory
kernelUsed += ionHeap;
}
- final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+ final long lostRAM = memInfo.getTotalSizeKb()
+ - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();
if (!opts.isCompact) {
- pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
+ pw.print(" Used RAM: "); pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss
+ kernelUsed)); pw.print(" (");
- pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
+ pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss)); pw.print(" used pss + ");
pw.print(stringifyKBSize(kernelUsed)); pw.print(" kernel)\n");
pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
} else {
@@ -12158,7 +11324,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private final void dumpApplicationMemoryUsage(FileDescriptor fd,
- MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
+ MemoryUsageDumpOptions opts, final String[] innerArgs, boolean brief,
ArrayList<ProcessRecord> procs) {
final long uptimeMs = SystemClock.uptimeMillis();
final long realtimeMs = SystemClock.elapsedRealtime();
@@ -12170,7 +11336,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (innerArgs.length > 0) {
proc = innerArgs[0];
if (proc.charAt(0) != '-') {
- ArrayList<ProcessCpuTracker.Stats> nativeProcs
+ final ArrayList<ProcessCpuTracker.Stats> nativeProcs
= new ArrayList<ProcessCpuTracker.Stats>();
updateCpuStatsNow();
int findPid = -1;
@@ -12178,16 +11344,13 @@ public class ActivityManagerService extends IActivityManager.Stub
findPid = Integer.parseInt(innerArgs[0]);
} catch (NumberFormatException e) {
}
- synchronized (mProcessCpuTracker) {
- final int N = mProcessCpuTracker.countStats();
- for (int i=0; i<N; i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- if (st.pid == findPid || (st.baseName != null
- && st.baseName.equals(innerArgs[0]))) {
- nativeProcs.add(st);
- }
+ final int fFindPid = findPid;
+ mAppProfiler.forAllCpuStats((st) -> {
+ if (st.pid == fFindPid || (st.baseName != null
+ && st.baseName.equals(innerArgs[0]))) {
+ nativeProcs.add(st);
}
- }
+ });
if (nativeProcs.size() > 0) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
@@ -12251,36 +11414,25 @@ public class ActivityManagerService extends IActivityManager.Stub
proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs);
proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs);
- ArrayList<MemItem> procMems = new ArrayList<MemItem>();
+ final ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
- long nativePss = 0;
- long nativeSwapPss = 0;
- long nativeRss = 0;
- long dalvikPss = 0;
- long dalvikSwapPss = 0;
- long dalvikRss = 0;
+ final long[] ss = new long[INDEX_LAST];
long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
long[] dalvikSubitemRss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
- long otherPss = 0;
- long otherSwapPss = 0;
- long otherRss = 0;
long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
- long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
- long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
- long[] oomRss = new long[DUMP_MEM_OOM_LABEL.length];
- ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
+ final long[] oomPss = new long[DUMP_MEM_OOM_LABEL.length];
+ final long[] oomSwapPss = new long[DUMP_MEM_OOM_LABEL.length];
+ final long[] oomRss = new long[DUMP_MEM_OOM_LABEL.length];
+ final ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
new ArrayList[DUMP_MEM_OOM_LABEL.length];
- long totalPss = 0;
- long totalSwapPss = 0;
- long totalRss = 0;
long cachedPss = 0;
long cachedSwapPss = 0;
boolean hasSwapPss = false;
@@ -12381,40 +11533,40 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (!opts.isCheckinRequest && mi != null) {
- totalPss += myTotalPss;
- totalSwapPss += myTotalSwapPss;
- totalRss += myTotalRss;
+ ss[INDEX_TOTAL_PSS] += myTotalPss;
+ ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+ ss[INDEX_TOTAL_RSS] += myTotalRss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
myTotalSwapPss, myTotalRss, pid, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
- nativePss += mi.nativePss;
- nativeSwapPss += mi.nativeSwappedOutPss;
- nativeRss += mi.nativeRss;
- dalvikPss += mi.dalvikPss;
- dalvikSwapPss += mi.dalvikSwappedOutPss;
- dalvikRss += mi.dalvikRss;
+ ss[INDEX_NATIVE_PSS] += mi.nativePss;
+ ss[INDEX_NATIVE_SWAP_PSS] += mi.nativeSwappedOutPss;
+ ss[INDEX_NATIVE_RSS] += mi.nativeRss;
+ ss[INDEX_DALVIK_PSS] += mi.dalvikPss;
+ ss[INDEX_DALVIK_SWAP_PSS] += mi.dalvikSwappedOutPss;
+ ss[INDEX_DALVIK_RSS] += mi.dalvikRss;
for (int j=0; j<dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemSwapPss[j] +=
mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
- otherPss += mi.otherPss;
- otherRss += mi.otherRss;
- otherSwapPss += mi.otherSwappedOutPss;
+ ss[INDEX_OTHER_PSS] += mi.otherPss;
+ ss[INDEX_OTHER_RSS] += mi.otherRss;
+ ss[INDEX_OTHER_SWAP_PSS] += mi.otherSwappedOutPss;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
- otherPss -= mem;
+ ss[INDEX_OTHER_PSS] -= mem;
mem = mi.getOtherSwappedOutPss(j);
miscSwapPss[j] += mem;
- otherSwapPss -= mem;
+ ss[INDEX_OTHER_SWAP_PSS] -= mem;
mem = mi.getOtherRss(j);
miscRss[j] += mem;
- otherRss -= mem;
+ ss[INDEX_OTHER_RSS] -= mem;
}
if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
@@ -12443,86 +11595,87 @@ public class ActivityManagerService extends IActivityManager.Stub
if (collectNative) {
mi = null;
- synchronized (mProcessCpuTracker) {
- final int N = mProcessCpuTracker.countStats();
- for (int i=0; i<N; i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
- if (mi == null) {
- mi = new Debug.MemoryInfo();
+ final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
+ mAppProfiler.forAllCpuStats((st) -> {
+ if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+ if (memInfos[0] == null) {
+ memInfos[0] = new Debug.MemoryInfo();
+ }
+ final Debug.MemoryInfo info = memInfos[0];
+ if (!brief && !opts.oomOnly) {
+ if (!Debug.getMemoryInfo(st.pid, info)) {
+ return;
}
- if (!brief && !opts.oomOnly) {
- if (!Debug.getMemoryInfo(st.pid, mi)) {
- continue;
- }
- } else {
- long pss = Debug.getPss(st.pid, tmpLong, null);
- if (pss == 0) {
- continue;
- }
- mi.nativePss = (int) pss;
- mi.nativePrivateDirty = (int) tmpLong[0];
- mi.nativeRss = (int) tmpLong[2];
+ } else {
+ long pss = Debug.getPss(st.pid, tmpLong, null);
+ if (pss == 0) {
+ return;
}
+ info.nativePss = (int) pss;
+ info.nativePrivateDirty = (int) tmpLong[0];
+ info.nativeRss = (int) tmpLong[2];
+ }
- final long myTotalPss = mi.getTotalPss();
- final long myTotalSwapPss = mi.getTotalSwappedOutPss();
- final long myTotalRss = mi.getTotalRss();
- totalPss += myTotalPss;
- totalSwapPss += myTotalSwapPss;
- totalRss += myTotalRss;
- nativeProcTotalPss += myTotalPss;
-
- MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
- st.name, myTotalPss, mi.getSummaryTotalSwapPss(), myTotalRss,
- st.pid, false);
- procMems.add(pssItem);
-
- nativePss += mi.nativePss;
- nativeSwapPss += mi.nativeSwappedOutPss;
- nativeRss += mi.nativeRss;
- dalvikPss += mi.dalvikPss;
- dalvikSwapPss += mi.dalvikSwappedOutPss;
- dalvikRss += mi.dalvikRss;
- for (int j=0; j<dalvikSubitemPss.length; j++) {
- dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
- dalvikSubitemSwapPss[j] +=
- mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
- dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
- + j);
- }
- otherPss += mi.otherPss;
- otherSwapPss += mi.otherSwappedOutPss;
- otherRss += mi.otherRss;
- for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
- long mem = mi.getOtherPss(j);
- miscPss[j] += mem;
- otherPss -= mem;
- mem = mi.getOtherSwappedOutPss(j);
- miscSwapPss[j] += mem;
- otherSwapPss -= mem;
- mem = mi.getOtherRss(j);
- miscRss[j] += mem;
- otherRss -= mem;
- }
- oomPss[0] += myTotalPss;
- oomSwapPss[0] += myTotalSwapPss;
- if (oomProcs[0] == null) {
- oomProcs[0] = new ArrayList<MemItem>();
- }
- oomProcs[0].add(pssItem);
- oomRss[0] += myTotalRss;
+ final long myTotalPss = info.getTotalPss();
+ final long myTotalSwapPss = info.getTotalSwappedOutPss();
+ final long myTotalRss = info.getTotalRss();
+ ss[INDEX_TOTAL_PSS] += myTotalPss;
+ ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+ ss[INDEX_TOTAL_RSS] += myTotalRss;
+ ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
+
+ MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
+ st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
+ st.pid, false);
+ procMems.add(pssItem);
+
+ ss[INDEX_NATIVE_PSS] += info.nativePss;
+ ss[INDEX_NATIVE_SWAP_PSS] += info.nativeSwappedOutPss;
+ ss[INDEX_NATIVE_RSS] += info.nativeRss;
+ ss[INDEX_DALVIK_PSS] += info.dalvikPss;
+ ss[INDEX_DALVIK_SWAP_PSS] += info.dalvikSwappedOutPss;
+ ss[INDEX_DALVIK_RSS] += info.dalvikRss;
+ for (int j = 0; j < dalvikSubitemPss.length; j++) {
+ dalvikSubitemPss[j] += info.getOtherPss(
+ Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemSwapPss[j] +=
+ info.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemRss[j] += info.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
+ + j);
}
+ ss[INDEX_OTHER_PSS] += info.otherPss;
+ ss[INDEX_OTHER_SWAP_PSS] += info.otherSwappedOutPss;
+ ss[INDEX_OTHER_RSS] += info.otherRss;
+ for (int j = 0; j < Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+ long mem = info.getOtherPss(j);
+ miscPss[j] += mem;
+ ss[INDEX_OTHER_PSS] -= mem;
+ mem = info.getOtherSwappedOutPss(j);
+ miscSwapPss[j] += mem;
+ ss[INDEX_OTHER_SWAP_PSS] -= mem;
+ mem = info.getOtherRss(j);
+ miscRss[j] += mem;
+ ss[INDEX_OTHER_RSS] -= mem;
+ }
+ oomPss[0] += myTotalPss;
+ oomSwapPss[0] += myTotalSwapPss;
+ if (oomProcs[0] == null) {
+ oomProcs[0] = new ArrayList<MemItem>();
+ }
+ oomProcs[0].add(pssItem);
+ oomRss[0] += myTotalRss;
}
- }
+ });
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
- catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, nativeRss, -1));
+ catMems.add(new MemItem("Native", "Native", ss[INDEX_NATIVE_PSS],
+ ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
final int dalvikId = -2;
- catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikRss,
- dalvikId));
- catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, otherRss, -3));
+ catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS],
+ ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId));
+ catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS],
+ ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3));
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
String label = Debug.MemoryInfo.getOtherLabel(j);
catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j));
@@ -12579,7 +11732,7 @@ public class ActivityManagerService extends IActivityManager.Stub
catMems, true, false, false);
}
- opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
+ opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && ss[INDEX_TOTAL_SWAP_PSS] != 0;
if (!opts.oomOnly) {
dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_PROCESS, "proc",
procMems, true, true, opts.dumpSwapPss);
@@ -12592,29 +11745,30 @@ public class ActivityManagerService extends IActivityManager.Stub
}
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
- if (nativeProcTotalPss > 0) {
+ if (ss[INDEX_TOTAL_NATIVE_PSS] > 0) {
synchronized (mProcessStats.mLock) {
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
final long kernelKb = memInfo.getKernelUsedSizeKb();
- EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
- kernelKb*1024, nativeProcTotalPss*1024);
+ EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+ kernelKb * 1024, ss[INDEX_TOTAL_NATIVE_PSS] * 1024);
mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
- nativeProcTotalPss);
+ ss[INDEX_TOTAL_NATIVE_PSS]);
}
}
if (!brief) {
proto.write(MemInfoDumpProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
- proto.write(MemInfoDumpProto.STATUS, mLastMemoryLevel);
+ proto.write(MemInfoDumpProto.STATUS, mAppProfiler.getLastMemoryLevelLocked());
proto.write(MemInfoDumpProto.CACHED_PSS_KB, cachedPss);
proto.write(MemInfoDumpProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
proto.write(MemInfoDumpProto.FREE_KB, memInfo.getFreeSizeKb());
}
- long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+ long lostRAM = memInfo.getTotalSizeKb()
+ - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
- proto.write(MemInfoDumpProto.USED_PSS_KB, totalPss - cachedPss);
+ proto.write(MemInfoDumpProto.USED_PSS_KB, ss[INDEX_TOTAL_PSS] - cachedPss);
proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
proto.write(MemInfoDumpProto.LOST_RAM_KB, lostRAM);
if (!brief) {
@@ -12685,13 +11839,8 @@ public class ActivityManagerService extends IActivityManager.Stub
updateCpuStatsNow();
long[] memtrackTmp = new long[1];
long[] swaptrackTmp = new long[2];
- final List<ProcessCpuTracker.Stats> stats;
// Get a list of Stats that have vsize > 0
- synchronized (mProcessCpuTracker) {
- stats = mProcessCpuTracker.getStats((st) -> {
- return st.vsize > 0;
- });
- }
+ final List<ProcessCpuTracker.Stats> stats = mAppProfiler.getCpuStats(st -> st.vsize > 0);
final int statsCount = stats.size();
for (int i = 0; i < statsCount; i++) {
ProcessCpuTracker.Stats st = stats.get(i);
@@ -12986,8 +12135,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mProcessesToGc.remove(app);
- mPendingPssProcesses.remove(app);
- ProcessList.abortNextPssTime(app.procStateMemTracker);
+ mAppProfiler.onCleanupApplicationRecordLocked(app);
// Dismiss any open dialogs.
app.getDialogController().clearAllErrorDialogs();
@@ -15538,229 +14686,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- /** @hide */
- public static Uri makeHeapDumpUri(String procName) {
- return Uri.parse("content://com.android.shell.heapdump/" + procName + "_javaheap.bin");
- }
-
- private final class RecordPssRunnable implements Runnable {
- private final ProcessRecord mProc;
- private final Uri mDumpUri;
- private final ContentResolver mContentResolver;
-
- RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) {
- mProc = proc;
- mDumpUri = dumpUri;
- mContentResolver = contentResolver;
- }
-
- @Override
- public void run() {
- try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) {
- IApplicationThread thread = mProc.thread;
- if (thread != null) {
- try {
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "Requesting dump heap from "
- + mProc + " to " + mDumpUri.getPath());
- }
- thread.dumpHeap(/* managed= */ true,
- /* mallocInfo= */ false, /* runGc= */ false,
- mDumpUri.getPath(), fd,
- /* finishCallback= */ null);
- } catch (RemoteException e) {
- }
- }
- } catch (IOException e) {
- Slog.e(TAG, "Failed to dump heap", e);
- // Need to clear the heap dump variables, otherwise no further heap dumps will be
- // attempted.
- abortHeapDump(mProc.processName);
- }
- }
- }
-
- /**
- * Record new PSS sample for a process.
- */
- void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss,
- long rss, int statType, long pssDuration, long now) {
- EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
- swapPss * 1024, rss * 1024, statType, procState, pssDuration);
- proc.lastPssTime = now;
- synchronized (mProcessStats.mLock) {
- proc.baseProcessTracker.addPss(
- pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
- }
- for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- pss, uss, rss, statType, pssDuration,
- holder.appVersion);
- }
- if (DEBUG_PSS) Slog.d(TAG_PSS,
- "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
- + " state=" + ProcessList.makeProcStateString(procState));
- if (proc.initialIdlePss == 0) {
- proc.initialIdlePss = pss;
- }
- proc.lastPss = pss;
- proc.lastSwapPss = swapPss;
- if (procState >= ActivityManager.PROCESS_STATE_HOME) {
- proc.lastCachedPss = pss;
- proc.lastCachedSwapPss = swapPss;
- }
- proc.mLastRss = rss;
-
- final SparseArray<Pair<Long, String>> watchUids
- = mMemWatchProcesses.getMap().get(proc.processName);
- Long check = null;
- if (watchUids != null) {
- Pair<Long, String> val = watchUids.get(proc.uid);
- if (val == null) {
- val = watchUids.get(0);
- }
- if (val != null) {
- check = val.first;
- }
- }
- if (check != null) {
- if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
- if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- isDebuggable = true;
- }
- }
- if (isDebuggable) {
- Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
- startHeapDumpLocked(proc, false);
- } else {
- Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
- + ", but debugging not enabled");
- }
- }
- }
- }
-
- private void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) {
- mMemWatchDumpProcName = proc.processName;
- mMemWatchDumpUri = makeHeapDumpUri(proc.processName);
- mMemWatchDumpPid = proc.pid;
- mMemWatchDumpUid = proc.uid;
- mMemWatchIsUserInitiated = isUserInitiated;
- Context ctx;
- try {
- ctx = mContext.createPackageContextAsUser("android", 0,
- UserHandle.getUserHandleForUid(mMemWatchDumpUid));
- } catch (NameNotFoundException e) {
- throw new RuntimeException("android package not found.");
- }
- BackgroundThread.getHandler().post(
- new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver()));
- }
-
- /**
- * Schedule PSS collection of a process.
- */
- boolean requestPssLocked(ProcessRecord proc, int procState) {
- if (mPendingPssProcesses.contains(proc)) {
- return false;
- }
- if (mPendingPssProcesses.size() == 0) {
- final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
- ? mPssDeferralTime : 0;
- if (DEBUG_PSS && deferral > 0) {
- Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
- + deferral + " ms");
- }
- mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, deferral);
- }
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
- proc.pssProcState = procState;
- proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
- mPendingPssProcesses.add(proc);
- return true;
- }
-
- /**
- * Re-defer a posted PSS collection pass, if one exists. Assumes deferral is
- * currently active policy when called.
- */
- private void deferPssIfNeededLocked() {
- if (mPendingPssProcesses.size() > 0) {
- mBgHandler.removeMessages(COLLECT_PSS_BG_MSG);
- mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, mPssDeferralTime);
- }
- }
-
- private void deferPssForActivityStart() {
- synchronized (ActivityManagerService.this) {
- if (mPssDeferralTime > 0) {
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
- }
- deferPssIfNeededLocked();
- mActivityStartingNesting.getAndIncrement();
- mBgHandler.sendEmptyMessageDelayed(STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
- }
- }
- }
-
- /**
- * Schedule PSS collection of all processes.
- */
- void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
- if (!always) {
- if (now < (mLastFullPssTime +
- (memLowered ? mConstants.FULL_PSS_LOWERED_INTERVAL
- : mConstants.FULL_PSS_MIN_INTERVAL))) {
- return;
- }
- }
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
- mLastFullPssTime = now;
- mFullPssPending = true;
- for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
- ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
- }
- mPendingPssProcesses.ensureCapacity(mProcessList.getLruSizeLocked());
- mPendingPssProcesses.clear();
- for (int i = mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
- continue;
- }
- if (memLowered || (always && now >
- app.lastStateTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
- || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
- app.pssProcState = app.setProcState;
- app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
- : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
- app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
- app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
- mPendingPssProcesses.add(app);
- }
- }
- if (!mBgHandler.hasMessages(COLLECT_PSS_BG_MSG)) {
- mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
- }
- }
-
- public void setTestPssMode(boolean enabled) {
- synchronized (this) {
- mTestPssMode = enabled;
- if (enabled) {
- // Whenever we enable the mode, we want to take a snapshot all of current
- // process mem use.
- requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
- }
- }
- }
-
/**
* Ask a given process to GC right now.
*/
@@ -16116,187 +15041,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@GuardedBy("this")
- final boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
- final int N = mProcessList.getLruSizeLocked();
- final long now = SystemClock.uptimeMillis();
- int memFactor;
- if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
- memFactor = mLowMemDetector.getMemFactor();
- } else {
- // Now determine the memory trimming level of background processes.
- // Unfortunately we need to start at the back of the list to do this
- // properly. We only do this if the number of background apps we
- // are managing to keep around is less than half the maximum we desire;
- // if we are keeping a good number around, we'll let them use whatever
- // memory they want.
- if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
- && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
- final int numCachedAndEmpty = numCached + numEmpty;
- if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
- } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
- } else {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
- }
- } else {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
- }
- }
- // We always allow the memory level to go up (better). We only allow it to go
- // down if we are in a state where that is allowed, *and* the total number of processes
- // has gone down since last time.
- if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
- + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
- + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
- if (memFactor > mLastMemoryLevel) {
- if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
- memFactor = mLastMemoryLevel;
- if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
- }
- }
- if (memFactor != mLastMemoryLevel) {
- EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
- FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
- }
- mLastMemoryLevel = memFactor;
- mLastNumProcesses = mProcessList.getLruSizeLocked();
- boolean allChanged;
- int trackerMemFactor;
- synchronized (mProcessStats.mLock) {
- allChanged = mProcessStats.setMemFactorLocked(
- memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
- trackerMemFactor = mProcessStats.getMemFactorLocked();
- }
- if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
- if (mLowRamStartTime == 0) {
- mLowRamStartTime = now;
- }
- int step = 0;
- int fgTrimLevel;
- switch (memFactor) {
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
- fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
- break;
- default:
- fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
- break;
- }
- int factor = numTrimming/3;
- int minFactor = 2;
- if (mAtmInternal.getHomeProcess() != null) minFactor++;
- if (mAtmInternal.getPreviousProcess() != null) minFactor++;
- if (factor < minFactor) factor = minFactor;
- int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (allChanged || app.procStateChanged) {
- setProcessTrackerStateLocked(app, trackerMemFactor, now);
- app.procStateChanged = false;
- }
- if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
- && !app.killedByAm) {
- if (app.trimMemoryLevel < curLevel && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of " + app.processName + " to " + curLevel);
- app.thread.scheduleTrimMemory(curLevel);
- } catch (RemoteException e) {
- }
- }
- app.trimMemoryLevel = curLevel;
- step++;
- if (step >= factor) {
- step = 0;
- switch (curLevel) {
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
- break;
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
- break;
- }
- }
- } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
- && !app.killedByAm) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
- && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of heavy-weight " + app.processName
- + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- app.thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- } catch (RemoteException e) {
- }
- }
- app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
- } else {
- if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.hasPendingUiClean()) {
- // If this application is now in the background and it
- // had done UI, then give it the special trim level to
- // have it free UI resources.
- final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- if (app.trimMemoryLevel < level && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of bg-ui " + app.processName
- + " to " + level);
- app.thread.scheduleTrimMemory(level);
- } catch (RemoteException e) {
- }
- }
- app.setPendingUiClean(false);
- }
- if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of fg " + app.processName
- + " to " + fgTrimLevel);
- app.thread.scheduleTrimMemory(fgTrimLevel);
- } catch (RemoteException e) {
- }
- }
- app.trimMemoryLevel = fgTrimLevel;
- }
- }
- } else {
- if (mLowRamStartTime != 0) {
- mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
- mLowRamStartTime = 0;
- }
- for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (allChanged || app.procStateChanged) {
- setProcessTrackerStateLocked(app, trackerMemFactor, now);
- app.procStateChanged = false;
- }
- if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.hasPendingUiClean()) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
- && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of ui hidden " + app.processName
- + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- app.thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- } catch (RemoteException e) {
- }
- }
- app.setPendingUiClean(false);
- }
- app.trimMemoryLevel = 0;
- }
- }
- return allChanged;
- }
-
- @GuardedBy("this")
final void updateOomAdjLocked(String oomAdjReason) {
mOomAdjuster.updateOomAdjLocked(oomAdjReason);
}
@@ -16584,109 +15328,31 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private void stopProfilerLocked(ProcessRecord proc, int profileType) {
- if (proc == null || proc == mProfileData.getProfileProc()) {
- proc = mProfileData.getProfileProc();
- profileType = mProfileType;
- clearProfilerLocked();
- }
- if (proc == null) {
- return;
- }
- try {
- proc.thread.profilerControl(false, null, profileType);
- } catch (RemoteException e) {
- throw new IllegalStateException("Process disappeared");
- }
- }
-
- void clearProfilerLocked() {
- if (mProfileData.getProfilerInfo() != null
- && mProfileData.getProfilerInfo().profileFd != null) {
- try {
- mProfileData.getProfilerInfo().profileFd.close();
- } catch (IOException e) {
- }
- }
- mProfileData.setProfileApp(null);
- mProfileData.setProfileProc(null);
- mProfileData.setProfilerInfo(null);
- }
-
public boolean profileControl(String process, int userId, boolean start,
ProfilerInfo profilerInfo, int profileType) throws RemoteException {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
- try {
- synchronized (this) {
- // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
- // its own permission.
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
-
- if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
- throw new IllegalArgumentException("null profile info or fd");
- }
-
- ProcessRecord proc = null;
- if (process != null) {
- proc = findProcessLocked(process, userId, "profileControl");
- }
-
- if (start && (proc == null || proc.thread == null)) {
- throw new IllegalArgumentException("Unknown process: " + process);
- }
-
- if (start) {
- stopProfilerLocked(null, 0);
- setProfileApp(proc.info, proc.processName, profilerInfo);
- mProfileData.setProfileProc(proc);
- mProfileType = profileType;
- ParcelFileDescriptor fd = profilerInfo.profileFd;
- try {
- fd = fd.dup();
- } catch (IOException e) {
- fd = null;
- }
- profilerInfo.profileFd = fd;
- proc.thread.profilerControl(start, profilerInfo, profileType);
- fd = null;
- try {
- mProfileData.getProfilerInfo().profileFd.close();
- } catch (IOException e) {
- }
- mProfileData.getProfilerInfo().profileFd = null;
-
- if (proc.pid == MY_PID) {
- // When profiling the system server itself, avoid closing the file
- // descriptor, as profilerControl will not create a copy.
- // Note: it is also not correct to just set profileFd to null, as the
- // whole ProfilerInfo instance is passed down!
- profilerInfo = null;
- }
- } else {
- stopProfilerLocked(proc, profileType);
- if (profilerInfo != null && profilerInfo.profileFd != null) {
- try {
- profilerInfo.profileFd.close();
- } catch (IOException e) {
- }
- }
- }
+ if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
+ throw new IllegalArgumentException("null profile info or fd");
+ }
- return true;
+ ProcessRecord proc = null;
+ if (process != null) {
+ proc = findProcessLocked(process, userId, "profileControl");
}
- } catch (RemoteException e) {
- throw new IllegalStateException("Process disappeared");
- } finally {
- if (profilerInfo != null && profilerInfo.profileFd != null) {
- try {
- profilerInfo.profileFd.close();
- } catch (IOException e) {
- }
+
+ if (start && (proc == null || proc.thread == null)) {
+ throw new IllegalArgumentException("Unknown process: " + process);
}
+
+ return mAppProfiler.profileControlLocked(proc, start, profilerInfo, profileType);
}
}
@@ -16746,7 +15412,7 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("Unknown process: " + process);
}
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+ boolean isDebuggable = Build.IS_DEBUGGABLE;
if (!isDebuggable) {
if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + proc);
@@ -16805,45 +15471,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
- synchronized (this) {
- if (maxMemSize > 0) {
- mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
- } else {
- if (uid != 0) {
- mMemWatchProcesses.remove(processName, uid);
- } else {
- mMemWatchProcesses.getMap().remove(processName);
- }
- }
- }
+ mAppProfiler.setDumpHeapDebugLimit(processName, uid, maxMemSize, reportPackage);
}
@Override
public void dumpHeapFinished(String path) {
- synchronized (this) {
- if (Binder.getCallingPid() != mMemWatchDumpPid) {
- Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid()
- + " does not match last pid " + mMemWatchDumpPid);
- return;
- }
- if (mMemWatchDumpUri == null || !mMemWatchDumpUri.getPath().equals(path)) {
- Slog.w(TAG, "dumpHeapFinished: Calling path " + path
- + " does not match last path " + mMemWatchDumpUri);
- return;
- }
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
- mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
-
- // Forced gc to clean up the remnant hprof fd.
- Runtime.getRuntime().gc();
- }
- }
-
- /** Clear the currently executing heap dump variables so a new heap dump can be started. */
- private void abortHeapDump(String procName) {
- Message msg = mHandler.obtainMessage(ABORT_DUMPHEAP_MSG);
- msg.obj = procName;
- mHandler.sendMessage(msg);
+ mAppProfiler.dumpHeapFinished(path, Binder.getCallingPid());
}
/** In this method we try to acquire our lock to make sure that we have not deadlocked */
@@ -16995,8 +15628,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
+ if (!Build.IS_DEBUGGABLE) {
if ((process.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
return false;
}
@@ -17426,7 +16058,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void updateCpuStats() {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateCpuStats();
+ ActivityManagerService.this.updateCpuStatsLocked();
}
}
@@ -18345,8 +16977,7 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("Unknown process: " + process);
}
- boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
- if (!isDebuggable) {
+ if (!Build.IS_DEBUGGABLE) {
if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + proc);
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 5d429d3065f3..3f55bffc1f3e 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -22,7 +22,6 @@ import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -36,10 +35,10 @@ import android.content.Intent;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -596,7 +595,7 @@ class AppErrors {
return mService.mAtmInternal.handleAppCrashInActivityController(
name, pid, shortMsg, longMsg, timeMillis, crashInfo.stackTrace, () -> {
- if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
+ if (Build.IS_DEBUGGABLE
&& "Native crash".equals(crashInfo.exceptionClassName)) {
Slog.w(TAG, "Skip killing native crashed app " + name
+ "(" + pid + ") during testing");
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
new file mode 100644
index 000000000000..0b5d5859e86e
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -0,0 +1,1731 @@
+/*
+ * Copyright (C) 2020 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.server.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+
+import android.annotation.BroadcastBehavior;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
+import android.content.ComponentCallbacks2;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.MemInfoReader;
+import com.android.server.am.ProcessList.ProcStateMemTracker;
+import com.android.server.utils.PriorityDump;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * A helper class taking care of the profiling, memory and cpu sampling of apps
+ */
+public class AppProfiler {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+
+ static final String TAG_PSS = TAG + POSTFIX_PSS;
+
+ static final String TAG_OOM_ADJ = ActivityManagerService.TAG_OOM_ADJ;
+
+ /** Control over CPU and battery monitoring */
+ // write battery stats every 30 minutes.
+ static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
+
+ static final boolean MONITOR_CPU_USAGE = true;
+
+ // don't sample cpu less than every 5 seconds.
+ static final long MONITOR_CPU_MIN_TIME = 5 * 1000;
+
+ // wait possibly forever for next cpu sample.
+ static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;
+
+ static final boolean MONITOR_THREAD_CPU_USAGE = false;
+
+ static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
+
+ /**
+ * Broadcast sent when heap dump collection has been completed.
+ */
+ @BroadcastBehavior(includeBackground = true, protectedBroadcast = true)
+ private static final String ACTION_HEAP_DUMP_FINISHED =
+ "com.android.internal.intent.action.HEAP_DUMP_FINISHED";
+
+ /**
+ * The process we are reporting
+ */
+ private static final String EXTRA_HEAP_DUMP_PROCESS_NAME =
+ "com.android.internal.extra.heap_dump.PROCESS_NAME";
+
+ /**
+ * The size limit the process reached.
+ */
+ private static final String EXTRA_HEAP_DUMP_SIZE_BYTES =
+ "com.android.internal.extra.heap_dump.SIZE_BYTES";
+
+ /**
+ * Whether the user initiated the dump or not.
+ */
+ private static final String EXTRA_HEAP_DUMP_IS_USER_INITIATED =
+ "com.android.internal.extra.heap_dump.IS_USER_INITIATED";
+
+ /**
+ * Optional name of package to directly launch.
+ */
+ private static final String EXTRA_HEAP_DUMP_REPORT_PACKAGE =
+ "com.android.internal.extra.heap_dump.REPORT_PACKAGE";
+
+ /**
+ * How long we defer PSS gathering while activities are starting, in milliseconds.
+ * This is adjustable via DeviceConfig. If it is zero or negative, no PSS deferral
+ * is done.
+ */
+ private volatile long mPssDeferralTime = 0;
+
+ /**
+ * Processes we want to collect PSS data from.
+ */
+ @GuardedBy("mService")
+ private final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
+
+ /**
+ * Depth of overlapping activity-start PSS deferral notes
+ */
+ private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
+
+ /**
+ * Last time we requested PSS data of all processes.
+ */
+ @GuardedBy("mService")
+ private long mLastFullPssTime = SystemClock.uptimeMillis();
+
+ /**
+ * If set, the next time we collect PSS data we should do a full collection
+ * with data from native processes and the kernel.
+ */
+ @GuardedBy("mService")
+ private boolean mFullPssPending = false;
+
+ /**
+ * If true, we are running under a test environment so will sample PSS from processes
+ * much more rapidly to try to collect better data when the tests are rapidly
+ * running through apps.
+ */
+ @GuardedBy("mService")
+ private boolean mTestPssMode = false;
+
+ @GuardedBy("mService")
+ private final LowMemDetector mLowMemDetector;
+
+ /**
+ * Allow the current computed overall memory level of the system to go down?
+ * This is set to false when we are killing processes for reasons other than
+ * memory management, so that the now smaller process list will not be taken as
+ * an indication that memory is tighter.
+ */
+ @GuardedBy("mService")
+ private boolean mAllowLowerMemLevel = false;
+
+ /**
+ * The last computed memory level, for holding when we are in a state that
+ * processes are going away for other reasons.
+ */
+ @GuardedBy("mService")
+ private int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+
+ /**
+ * The last total number of process we have, to determine if changes actually look
+ * like a shrinking number of process due to lower RAM.
+ */
+ @GuardedBy("mService")
+ private int mLastNumProcesses;
+
+ /**
+ * Total time spent with RAM that has been added in the past since the last idle time.
+ */
+ @GuardedBy("mService")
+ private long mLowRamTimeSinceLastIdle = 0;
+
+ /**
+ * If RAM is currently low, when that horrible situation started.
+ */
+ @GuardedBy("mService")
+ private long mLowRamStartTime = 0;
+
+ /**
+ * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+ * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+ * A non-null agent in mProfileInfo overrides this.
+ */
+ private @Nullable Map<String, String> mAppAgentMap = null;
+
+ private int mProfileType = 0;
+ private final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
+ private String mMemWatchDumpProcName;
+ private Uri mMemWatchDumpUri;
+ private int mMemWatchDumpPid;
+ private int mMemWatchDumpUid;
+ private boolean mMemWatchIsUserInitiated;
+
+ /**
+ * Used to collect per-process CPU use for ANRs, battery stats, etc.
+ * Must acquire this object's lock when accessing it.
+ * NOTE: this lock will be held while doing long operations (trawling
+ * through all processes in /proc), so it should never be acquired by
+ * any critical paths such as when holding the main activity manager lock.
+ */
+ private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
+ MONITOR_THREAD_CPU_USAGE);
+ private final AtomicLong mLastCpuTime = new AtomicLong(0);
+ private final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
+ private final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
+
+ private long mLastWriteTime = 0;
+ private final ProfileData mProfileData = new ProfileData();
+
+ /**
+ * Runtime CPU use collection thread. This object's lock is used to
+ * perform synchronization with the thread (notifying it to run).
+ */
+ private final Thread mProcessCpuThread;
+
+ private final ActivityManagerService mService;
+ private final Handler mBgHandler;
+
+ /**
+ * Observe DeviceConfig changes to the PSS calculation interval
+ */
+ private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(Properties properties) {
+ if (properties.getKeyset().contains(ACTIVITY_START_PSS_DEFER_CONFIG)) {
+ mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Activity-start PSS delay now "
+ + mPssDeferralTime + " ms");
+ }
+ }
+ }
+ };
+
+ private class ProfileData {
+ private String mProfileApp = null;
+ private ProcessRecord mProfileProc = null;
+ private ProfilerInfo mProfilerInfo = null;
+
+ void setProfileApp(String profileApp) {
+ mProfileApp = profileApp;
+ if (mService.mAtmInternal != null) {
+ mService.mAtmInternal.setProfileApp(profileApp);
+ }
+ }
+
+ String getProfileApp() {
+ return mProfileApp;
+ }
+
+ void setProfileProc(ProcessRecord profileProc) {
+ mProfileProc = profileProc;
+ if (mService.mAtmInternal != null) {
+ mService.mAtmInternal.setProfileProc(profileProc == null ? null
+ : profileProc.getWindowProcessController());
+ }
+ }
+
+ ProcessRecord getProfileProc() {
+ return mProfileProc;
+ }
+
+ void setProfilerInfo(ProfilerInfo profilerInfo) {
+ mProfilerInfo = profilerInfo;
+ if (mService.mAtmInternal != null) {
+ mService.mAtmInternal.setProfilerInfo(profilerInfo);
+ }
+ }
+
+ ProfilerInfo getProfilerInfo() {
+ return mProfilerInfo;
+ }
+ }
+
+ private class BgHandler extends Handler {
+ static final int COLLECT_PSS_BG_MSG = 1;
+ static final int DEFER_PSS_MSG = 2;
+ static final int STOP_DEFERRING_PSS_MSG = 3;
+ BgHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case COLLECT_PSS_BG_MSG:
+ collectPssInBackground();
+ break;
+ case DEFER_PSS_MSG:
+ deferPssForActivityStart();
+ break;
+ case STOP_DEFERRING_PSS_MSG:
+ stopDeferPss();
+ break;
+ }
+ }
+ }
+
+ private void collectPssInBackground() {
+ long start = SystemClock.uptimeMillis();
+ MemInfoReader memInfo = null;
+ synchronized (mService) {
+ if (mFullPssPending) {
+ mFullPssPending = false;
+ memInfo = new MemInfoReader();
+ }
+ }
+ if (memInfo != null) {
+ updateCpuStatsNow();
+ long nativeTotalPss = 0;
+ final List<ProcessCpuTracker.Stats> stats;
+ synchronized (mProcessCpuTracker) {
+ stats = mProcessCpuTracker.getStats(st -> {
+ return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
+ });
+ }
+ final int numOfStats = stats.size();
+ for (int j = 0; j < numOfStats; j++) {
+ synchronized (mService.mPidsSelfLocked) {
+ if (mService.mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
+ // This is one of our own processes; skip it.
+ continue;
+ }
+ }
+ nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
+ }
+ memInfo.readMemInfo();
+ synchronized (mService.mProcessStats.mLock) {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Collected native and kernel memory in "
+ + (SystemClock.uptimeMillis() - start) + "ms");
+ }
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+ kernelKb * 1024, nativeTotalPss * 1024);
+ mService.mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeTotalPss);
+ }
+ }
+
+ int num = 0;
+ long[] tmp = new long[3];
+ do {
+ ProcessRecord proc;
+ int procState;
+ int statType;
+ int pid = -1;
+ long lastPssTime;
+ synchronized (mService) {
+ if (mPendingPssProcesses.size() <= 0) {
+ if (mTestPssMode || DEBUG_PSS) {
+ Slog.d(TAG_PSS,
+ "Collected pss of " + num + " processes in "
+ + (SystemClock.uptimeMillis() - start) + "ms");
+ }
+ mPendingPssProcesses.clear();
+ return;
+ }
+ proc = mPendingPssProcesses.remove(0);
+ procState = proc.pssProcState;
+ statType = proc.pssStatType;
+ lastPssTime = proc.lastPssTime;
+ long now = SystemClock.uptimeMillis();
+ if (proc.thread != null && procState == proc.setProcState
+ && (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) < now) {
+ pid = proc.pid;
+ } else {
+ abortNextPssTime(proc.procStateMemTracker);
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+ + ": still need "
+ + (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE - now)
+ + "ms until safe");
+ }
+ proc = null;
+ pid = 0;
+ }
+ }
+ if (proc != null) {
+ long startTime = SystemClock.currentThreadTimeMillis();
+ // skip background PSS calculation of apps that are capturing
+ // camera imagery
+ final boolean usingCamera = mService.isCameraActiveForUid(proc.uid);
+ long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
+ long endTime = SystemClock.currentThreadTimeMillis();
+ synchronized (mService) {
+ if (pss != 0 && proc.thread != null && proc.setProcState == procState
+ && proc.pid == pid && proc.lastPssTime == lastPssTime) {
+ num++;
+ commitNextPssTime(proc.procStateMemTracker);
+ recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
+ statType, endTime - startTime, SystemClock.uptimeMillis());
+ } else {
+ abortNextPssTime(proc.procStateMemTracker);
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+ + ": " + (proc.thread == null ? "NO_THREAD " : "")
+ + (usingCamera ? "CAMERA " : "")
+ + (proc.pid != pid ? "PID_CHANGED " : "")
+ + " initState=" + procState + " curState="
+ + proc.setProcState + " "
+ + (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
+ }
+ }
+ }
+ }
+ } while (true);
+ }
+
+ private static void commitNextPssTime(ProcStateMemTracker tracker) {
+ if (tracker.mPendingMemState >= 0) {
+ tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
+ tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
+ tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
+ tracker.mPendingMemState = -1;
+ }
+ }
+
+ private static void abortNextPssTime(ProcStateMemTracker tracker) {
+ tracker.mPendingMemState = -1;
+ }
+
+ @GuardedBy("mService")
+ void updateNextPssTimeLocked(int procState, ProcessRecord app, long now, boolean forceUpdate) {
+ if (!forceUpdate) {
+ if (now <= app.nextPssTime
+ && now <= Math.max(app.lastPssTime + ProcessList.PSS_MAX_INTERVAL,
+ app.lastStateTime + ProcessList.minTimeFromStateChange(mTestPssMode))) {
+ // update is not due, ignore it.
+ return;
+ }
+ if (!requestPssLocked(app, app.setProcState)) {
+ return;
+ }
+ }
+ app.nextPssTime = ProcessList.computeNextPssTime(procState, app.procStateMemTracker,
+ mTestPssMode, mService.mAtmInternal.isSleeping(), now);
+ }
+
+ /**
+ * Record new PSS sample for a process.
+ */
+ @GuardedBy("mService")
+ private void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss,
+ long swapPss, long rss, int statType, long pssDuration, long now) {
+ EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
+ swapPss * 1024, rss * 1024, statType, procState, pssDuration);
+ proc.lastPssTime = now;
+ synchronized (mService.mProcessStats.mLock) {
+ proc.baseProcessTracker.addPss(
+ pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
+ }
+ for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ pss, uss, rss, statType, pssDuration,
+ holder.appVersion);
+ }
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS,
+ "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ + " state=" + ProcessList.makeProcStateString(procState));
+ }
+ if (proc.initialIdlePss == 0) {
+ proc.initialIdlePss = pss;
+ }
+ proc.lastPss = pss;
+ proc.lastSwapPss = swapPss;
+ if (procState >= ActivityManager.PROCESS_STATE_HOME) {
+ proc.lastCachedPss = pss;
+ proc.lastCachedSwapPss = swapPss;
+ }
+ proc.mLastRss = rss;
+
+ final SparseArray<Pair<Long, String>> watchUids =
+ mMemWatchProcesses.getMap().get(proc.processName);
+ Long check = null;
+ if (watchUids != null) {
+ Pair<Long, String> val = watchUids.get(proc.uid);
+ if (val == null) {
+ val = watchUids.get(0);
+ }
+ if (val != null) {
+ check = val.first;
+ }
+ }
+ if (check != null) {
+ if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
+ boolean isDebuggable = Build.IS_DEBUGGABLE;
+ if (!isDebuggable) {
+ if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ isDebuggable = true;
+ }
+ }
+ if (isDebuggable) {
+ Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
+ startHeapDumpLocked(proc, false);
+ } else {
+ Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
+ + ", but debugging not enabled");
+ }
+ }
+ }
+ }
+
+ private final class RecordPssRunnable implements Runnable {
+ private final ProcessRecord mProc;
+ private final Uri mDumpUri;
+ private final ContentResolver mContentResolver;
+
+ RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) {
+ mProc = proc;
+ mDumpUri = dumpUri;
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public void run() {
+ try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) {
+ IApplicationThread thread = mProc.thread;
+ if (thread != null) {
+ try {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Requesting dump heap from "
+ + mProc + " to " + mDumpUri.getPath());
+ }
+ thread.dumpHeap(/* managed= */ true,
+ /* mallocInfo= */ false, /* runGc= */ false,
+ mDumpUri.getPath(), fd,
+ /* finishCallback= */ null);
+ } catch (RemoteException e) {
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to dump heap", e);
+ // Need to clear the heap dump variables, otherwise no further heap dumps will be
+ // attempted.
+ abortHeapDump(mProc.processName);
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) {
+ mMemWatchDumpProcName = proc.processName;
+ mMemWatchDumpUri = makeHeapDumpUri(proc.processName);
+ mMemWatchDumpPid = proc.pid;
+ mMemWatchDumpUid = proc.uid;
+ mMemWatchIsUserInitiated = isUserInitiated;
+ Context ctx;
+ try {
+ ctx = mService.mContext.createPackageContextAsUser("android", 0,
+ UserHandle.getUserHandleForUid(mMemWatchDumpUid));
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException("android package not found.");
+ }
+ BackgroundThread.getHandler().post(
+ new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver()));
+ }
+
+ void dumpHeapFinished(String path, int callerPid) {
+ synchronized (mService) {
+ if (callerPid != mMemWatchDumpPid) {
+ Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid()
+ + " does not match last pid " + mMemWatchDumpPid);
+ return;
+ }
+ if (mMemWatchDumpUri == null || !mMemWatchDumpUri.getPath().equals(path)) {
+ Slog.w(TAG, "dumpHeapFinished: Calling path " + path
+ + " does not match last path " + mMemWatchDumpUri);
+ return;
+ }
+ if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
+ mService.mHandler.sendEmptyMessage(
+ ActivityManagerService.POST_DUMP_HEAP_NOTIFICATION_MSG);
+
+ // Forced gc to clean up the remnant hprof fd.
+ Runtime.getRuntime().gc();
+ }
+ }
+
+ void handlePostDumpHeapNotification() {
+ final String procName;
+ final int uid;
+ final long memLimit;
+ final String reportPackage;
+ final boolean isUserInitiated;
+ synchronized (mService) {
+ uid = mMemWatchDumpUid;
+ procName = mMemWatchDumpProcName;
+ Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
+ if (val == null) {
+ val = mMemWatchProcesses.get(procName, 0);
+ }
+ if (val != null) {
+ memLimit = val.first;
+ reportPackage = val.second;
+ } else {
+ memLimit = 0;
+ reportPackage = null;
+ }
+ isUserInitiated = mMemWatchIsUserInitiated;
+
+ mMemWatchDumpUri = null;
+ mMemWatchDumpProcName = null;
+ mMemWatchDumpPid = -1;
+ mMemWatchDumpUid = -1;
+ }
+ if (procName == null) {
+ return;
+ }
+
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Showing dump heap notification from " + procName + "/" + uid);
+ }
+
+ Intent dumpFinishedIntent = new Intent(ACTION_HEAP_DUMP_FINISHED);
+ // Send this only to the Shell package.
+ dumpFinishedIntent.setPackage("com.android.shell");
+ dumpFinishedIntent.putExtra(Intent.EXTRA_UID, uid);
+ dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_IS_USER_INITIATED, isUserInitiated);
+ dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_SIZE_BYTES, memLimit);
+ dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_REPORT_PACKAGE, reportPackage);
+ dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_PROCESS_NAME, procName);
+
+ mService.mContext.sendBroadcastAsUser(dumpFinishedIntent,
+ UserHandle.getUserHandleForUid(uid));
+ }
+
+ void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) {
+ synchronized (mService) {
+ if (maxMemSize > 0) {
+ mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
+ } else {
+ if (uid != 0) {
+ mMemWatchProcesses.remove(processName, uid);
+ } else {
+ mMemWatchProcesses.getMap().remove(processName);
+ }
+ }
+ }
+ }
+
+ /** Clear the currently executing heap dump variables so a new heap dump can be started. */
+ private void abortHeapDump(String procName) {
+ Message msg = mService.mHandler.obtainMessage(ActivityManagerService.ABORT_DUMPHEAP_MSG);
+ msg.obj = procName;
+ mService.mHandler.sendMessage(msg);
+ }
+
+ void handleAbortDumpHeap(String procName) {
+ if (procName != null) {
+ synchronized (mService) {
+ if (procName.equals(mMemWatchDumpProcName)) {
+ mMemWatchDumpProcName = null;
+ mMemWatchDumpUri = null;
+ mMemWatchDumpPid = -1;
+ mMemWatchDumpUid = -1;
+ }
+ }
+ }
+ }
+
+ /** @hide */
+ private static Uri makeHeapDumpUri(String procName) {
+ return Uri.parse("content://com.android.shell.heapdump/" + procName + "_javaheap.bin");
+ }
+
+ /**
+ * Schedule PSS collection of a process.
+ */
+ @GuardedBy("mService")
+ private boolean requestPssLocked(ProcessRecord proc, int procState) {
+ if (mPendingPssProcesses.contains(proc)) {
+ return false;
+ }
+ if (mPendingPssProcesses.size() == 0) {
+ final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
+ ? mPssDeferralTime : 0;
+ if (DEBUG_PSS && deferral > 0) {
+ Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+ + deferral + " ms");
+ }
+ mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, deferral);
+ }
+ if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
+ proc.pssProcState = procState;
+ proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
+ mPendingPssProcesses.add(proc);
+ return true;
+ }
+
+ /**
+ * Re-defer a posted PSS collection pass, if one exists. Assumes deferral is
+ * currently active policy when called.
+ */
+ @GuardedBy("mService")
+ private void deferPssIfNeededLocked() {
+ if (mPendingPssProcesses.size() > 0) {
+ mBgHandler.removeMessages(BgHandler.COLLECT_PSS_BG_MSG);
+ mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, mPssDeferralTime);
+ }
+ }
+
+ private void deferPssForActivityStart() {
+ if (mPssDeferralTime > 0) {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
+ }
+ deferPssIfNeededLocked();
+ mActivityStartingNesting.getAndIncrement();
+ mBgHandler.sendEmptyMessageDelayed(BgHandler.STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
+ }
+ }
+
+ private void stopDeferPss() {
+ final int nesting = mActivityStartingNesting.decrementAndGet();
+ if (nesting <= 0) {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
+ + nesting);
+ }
+ if (nesting < 0) {
+ Slog.wtf(TAG, "Activity start nesting undercount!");
+ mActivityStartingNesting.incrementAndGet();
+ }
+ } else {
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
+ }
+ }
+ }
+
+ /**
+ * Schedule PSS collection of all processes.
+ */
+ @GuardedBy("mService")
+ void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
+ if (!always) {
+ if (now < (mLastFullPssTime
+ + (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL
+ : mService.mConstants.FULL_PSS_MIN_INTERVAL))) {
+ return;
+ }
+ }
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
+ }
+ mLastFullPssTime = now;
+ mFullPssPending = true;
+ for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
+ abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);
+ }
+ mPendingPssProcesses.ensureCapacity(mService.mProcessList.getLruSizeLocked());
+ mPendingPssProcesses.clear();
+ for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+ ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
+ continue;
+ }
+ if (memLowered || (always && now
+ > app.lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
+ || now > (app.lastStateTime + ProcessList.PSS_ALL_INTERVAL)) {
+ app.pssProcState = app.setProcState;
+ app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
+ : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
+ updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
+ mPendingPssProcesses.add(app);
+ }
+ }
+ if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
+ mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
+ }
+ }
+
+ void setTestPssMode(boolean enabled) {
+ synchronized (mService) {
+ mTestPssMode = enabled;
+ if (enabled) {
+ // Whenever we enable the mode, we want to take a snapshot all of current
+ // process mem use.
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ boolean getTestPssModeLocked() {
+ return mTestPssMode;
+ }
+
+ @GuardedBy("mService")
+ int getLastMemoryLevelLocked() {
+ return mLastMemoryLevel;
+ }
+
+ @GuardedBy("mService")
+ boolean isLastMemoryLevelNormal() {
+ return mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ }
+
+ @GuardedBy("mService")
+ void updateLowRamTimestampLocked(long now) {
+ mLowRamTimeSinceLastIdle = 0;
+ if (mLowRamStartTime != 0) {
+ mLowRamStartTime = now;
+ }
+ }
+
+ @GuardedBy("mService")
+ void setAllowLowerMemLevelLocked(boolean allowLowerMemLevel) {
+ mAllowLowerMemLevel = allowLowerMemLevel;
+ }
+
+ @GuardedBy("mService")
+ boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
+ final int numOfLru = mService.mProcessList.getLruSizeLocked();
+ final long now = SystemClock.uptimeMillis();
+ int memFactor;
+ if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
+ memFactor = mLowMemDetector.getMemFactor();
+ } else {
+ // Now determine the memory trimming level of background processes.
+ // Unfortunately we need to start at the back of the list to do this
+ // properly. We only do this if the number of background apps we
+ // are managing to keep around is less than half the maximum we desire;
+ // if we are keeping a good number around, we'll let them use whatever
+ // memory they want.
+ if (numCached <= mService.mConstants.CUR_TRIM_CACHED_PROCESSES
+ && numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {
+ final int numCachedAndEmpty = numCached + numEmpty;
+ if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+ } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
+ } else {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+ }
+ } else {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ }
+ }
+ // We always allow the memory level to go up (better). We only allow it to go
+ // down if we are in a state where that is allowed, *and* the total number of processes
+ // has gone down since last time.
+ if (DEBUG_OOM_ADJ) {
+ Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+ + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
+ + " numProcs=" + mService.mProcessList.getLruSizeLocked()
+ + " last=" + mLastNumProcesses);
+ }
+ if (memFactor > mLastMemoryLevel) {
+ if (!mAllowLowerMemLevel
+ || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
+ memFactor = mLastMemoryLevel;
+ if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
+ }
+ }
+ if (memFactor != mLastMemoryLevel) {
+ EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
+ FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
+ }
+ mLastMemoryLevel = memFactor;
+ mLastNumProcesses = mService.mProcessList.getLruSizeLocked();
+ boolean allChanged;
+ int trackerMemFactor;
+ synchronized (mService.mProcessStats.mLock) {
+ allChanged = mService.mProcessStats.setMemFactorLocked(memFactor,
+ mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
+ trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
+ }
+ if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
+ if (mLowRamStartTime == 0) {
+ mLowRamStartTime = now;
+ }
+ int step = 0;
+ int fgTrimLevel;
+ switch (memFactor) {
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
+ break;
+ default:
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
+ break;
+ }
+ int factor = numTrimming / 3;
+ int minFactor = 2;
+ if (mService.mAtmInternal.getHomeProcess() != null) minFactor++;
+ if (mService.mAtmInternal.getPreviousProcess() != null) minFactor++;
+ if (factor < minFactor) factor = minFactor;
+ int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
+ for (int i = numOfLru - 1; i >= 0; i--) {
+ ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ if (allChanged || app.procStateChanged) {
+ mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
+ app.procStateChanged = false;
+ }
+ if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+ && !app.killedByAm) {
+ if (app.trimMemoryLevel < curLevel && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of " + app.processName
+ + " to " + curLevel);
+ }
+ app.thread.scheduleTrimMemory(curLevel);
+ } catch (RemoteException e) {
+ }
+ }
+ app.trimMemoryLevel = curLevel;
+ step++;
+ if (step >= factor) {
+ step = 0;
+ switch (curLevel) {
+ case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+ curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
+ break;
+ case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+ curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ break;
+ }
+ }
+ } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ && !app.killedByAm) {
+ if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
+ && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of heavy-weight " + app.processName
+ + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
+ }
+ app.thread.scheduleTrimMemory(
+ ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
+ } catch (RemoteException e) {
+ }
+ }
+ app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ } else {
+ if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || app.systemNoUi) && app.hasPendingUiClean()) {
+ // If this application is now in the background and it
+ // had done UI, then give it the special trim level to
+ // have it free UI resources.
+ final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+ if (app.trimMemoryLevel < level && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
+ + app.processName + " to " + level);
+ }
+ app.thread.scheduleTrimMemory(level);
+ } catch (RemoteException e) {
+ }
+ }
+ app.setPendingUiClean(false);
+ }
+ if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
+ + " to " + fgTrimLevel);
+ }
+ app.thread.scheduleTrimMemory(fgTrimLevel);
+ } catch (RemoteException e) {
+ }
+ }
+ app.trimMemoryLevel = fgTrimLevel;
+ }
+ }
+ } else {
+ if (mLowRamStartTime != 0) {
+ mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
+ mLowRamStartTime = 0;
+ }
+ for (int i = numOfLru - 1; i >= 0; i--) {
+ ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ if (allChanged || app.procStateChanged) {
+ mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
+ app.procStateChanged = false;
+ }
+ if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || app.systemNoUi) && app.hasPendingUiClean()) {
+ if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+ && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of ui hidden " + app.processName
+ + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ }
+ app.thread.scheduleTrimMemory(
+ ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ } catch (RemoteException e) {
+ }
+ }
+ app.setPendingUiClean(false);
+ }
+ app.trimMemoryLevel = 0;
+ }
+ }
+ return allChanged;
+ }
+
+ @GuardedBy("mService")
+ long getLowRamTimeSinceIdleLocked(long now) {
+ return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
+ }
+
+ @GuardedBy("mService")
+ private void stopProfilerLocked(ProcessRecord proc, int profileType) {
+ if (proc == null || proc == mProfileData.getProfileProc()) {
+ proc = mProfileData.getProfileProc();
+ profileType = mProfileType;
+ clearProfilerLocked();
+ }
+ if (proc == null) {
+ return;
+ }
+ try {
+ proc.thread.profilerControl(false, null, profileType);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ }
+ }
+
+ @GuardedBy("mService")
+ void clearProfilerLocked() {
+ if (mProfileData.getProfilerInfo() != null
+ && mProfileData.getProfilerInfo().profileFd != null) {
+ try {
+ mProfileData.getProfilerInfo().profileFd.close();
+ } catch (IOException e) {
+ }
+ }
+ mProfileData.setProfileApp(null);
+ mProfileData.setProfileProc(null);
+ mProfileData.setProfilerInfo(null);
+ }
+
+ @GuardedBy("mService")
+ void clearProfilerLocked(ProcessRecord app) {
+ if (mProfileData.getProfileProc() == null
+ || mProfileData.getProfilerInfo() == null
+ || mProfileData.getProfileProc() != app) {
+ return;
+ }
+ clearProfilerLocked();
+ }
+
+ @GuardedBy("mService")
+ boolean profileControlLocked(ProcessRecord proc, boolean start,
+ ProfilerInfo profilerInfo, int profileType) {
+ try {
+ if (start) {
+ stopProfilerLocked(null, 0);
+ mService.setProfileApp(proc.info, proc.processName, profilerInfo);
+ mProfileData.setProfileProc(proc);
+ mProfileType = profileType;
+ ParcelFileDescriptor fd = profilerInfo.profileFd;
+ try {
+ fd = fd.dup();
+ } catch (IOException e) {
+ fd = null;
+ }
+ profilerInfo.profileFd = fd;
+ proc.thread.profilerControl(start, profilerInfo, profileType);
+ fd = null;
+ try {
+ mProfileData.getProfilerInfo().profileFd.close();
+ } catch (IOException e) {
+ }
+ mProfileData.getProfilerInfo().profileFd = null;
+
+ if (proc.pid == mService.MY_PID) {
+ // When profiling the system server itself, avoid closing the file
+ // descriptor, as profilerControl will not create a copy.
+ // Note: it is also not correct to just set profileFd to null, as the
+ // whole ProfilerInfo instance is passed down!
+ profilerInfo = null;
+ }
+ } else {
+ stopProfilerLocked(proc, profileType);
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ try {
+ profilerInfo.profileFd.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ return true;
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ } finally {
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ try {
+ profilerInfo.profileFd.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void setProfileAppLocked(String processName, ProfilerInfo profilerInfo) {
+ mProfileData.setProfileApp(processName);
+
+ if (mProfileData.getProfilerInfo() != null) {
+ if (mProfileData.getProfilerInfo().profileFd != null) {
+ try {
+ mProfileData.getProfilerInfo().profileFd.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
+ mProfileType = 0;
+ }
+
+ @GuardedBy("mService")
+ void setProfileProcLocked(ProcessRecord proc) {
+ mProfileData.setProfileProc(proc);
+ }
+
+ @GuardedBy("mService")
+ void setAgentAppLocked(@NonNull String packageName, @Nullable String agent) {
+ if (agent == null) {
+ if (mAppAgentMap != null) {
+ mAppAgentMap.remove(packageName);
+ if (mAppAgentMap.isEmpty()) {
+ mAppAgentMap = null;
+ }
+ }
+ } else {
+ if (mAppAgentMap == null) {
+ mAppAgentMap = new HashMap<>();
+ }
+ if (mAppAgentMap.size() >= 100) {
+ // Limit the size of the map, to avoid OOMEs.
+ Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ + "/" + agent);
+ return;
+ }
+ mAppAgentMap.put(packageName, agent);
+ }
+ }
+
+ @GuardedBy("mService")
+ void updateCpuStatsLocked() {
+ final long now = SystemClock.uptimeMillis();
+ if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
+ return;
+ }
+ if (mProcessCpuMutexFree.compareAndSet(true, false)) {
+ synchronized (mProcessCpuThread) {
+ mProcessCpuThread.notify();
+ }
+ }
+ }
+
+ void updateCpuStatsNow() {
+ synchronized (mProcessCpuTracker) {
+ mProcessCpuMutexFree.set(false);
+ final long now = SystemClock.uptimeMillis();
+ boolean haveNewCpuStats = false;
+
+ if (MONITOR_CPU_USAGE
+ && mLastCpuTime.get() < (now - MONITOR_CPU_MIN_TIME)) {
+ mLastCpuTime.set(now);
+ mProcessCpuTracker.update();
+ if (mProcessCpuTracker.hasGoodLastStats()) {
+ haveNewCpuStats = true;
+ //Slog.i(TAG, mProcessCpu.printCurrentState());
+ //Slog.i(TAG, "Total CPU usage: "
+ // + mProcessCpu.getTotalCpuPercent() + "%");
+
+ // Slog the cpu usage if the property is set.
+ if ("true".equals(SystemProperties.get("events.cpu"))) {
+ int user = mProcessCpuTracker.getLastUserTime();
+ int system = mProcessCpuTracker.getLastSystemTime();
+ int iowait = mProcessCpuTracker.getLastIoWaitTime();
+ int irq = mProcessCpuTracker.getLastIrqTime();
+ int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
+ int idle = mProcessCpuTracker.getLastIdleTime();
+
+ int total = user + system + iowait + irq + softIrq + idle;
+ if (total == 0) total = 1;
+
+ EventLogTags.writeCpu(
+ ((user + system + iowait + irq + softIrq) * 100) / total,
+ (user * 100) / total,
+ (system * 100) / total,
+ (iowait * 100) / total,
+ (irq * 100) / total,
+ (softIrq * 100) / total);
+ }
+ }
+ }
+
+ final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics();
+ synchronized (bstats) {
+ if (haveNewCpuStats) {
+ if (bstats.startAddingCpuLocked()) {
+ int totalUTime = 0;
+ int totalSTime = 0;
+ final int statsCount = mProcessCpuTracker.countStats();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ synchronized (mService.mPidsSelfLocked) {
+ for (int i = 0; i < statsCount; i++) {
+ ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+ if (!st.working) {
+ continue;
+ }
+ ProcessRecord pr = mService.mPidsSelfLocked.get(st.pid);
+ totalUTime += st.rel_utime;
+ totalSTime += st.rel_stime;
+ if (pr != null) {
+ BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
+ if (ps == null || !ps.isActive()) {
+ pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
+ pr.info.uid, pr.processName,
+ elapsedRealtime, uptime);
+ }
+ ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+ pr.curCpuTime += st.rel_utime + st.rel_stime;
+ if (pr.lastCpuTime == 0) {
+ pr.lastCpuTime = pr.curCpuTime;
+ }
+ } else {
+ BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
+ if (ps == null || !ps.isActive()) {
+ st.batteryStats = ps = bstats.getProcessStatsLocked(
+ bstats.mapUid(st.uid), st.name,
+ elapsedRealtime, uptime);
+ }
+ ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+ }
+ }
+ }
+
+ final int userTime = mProcessCpuTracker.getLastUserTime();
+ final int systemTime = mProcessCpuTracker.getLastSystemTime();
+ final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime();
+ final int irqTime = mProcessCpuTracker.getLastIrqTime();
+ final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
+ final int idleTime = mProcessCpuTracker.getLastIdleTime();
+ bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
+ systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
+ }
+ }
+
+ if (mLastWriteTime < (now - BATTERY_STATS_TIME)) {
+ mLastWriteTime = now;
+ mService.mBatteryStatsService.scheduleWriteToDisk();
+ }
+ }
+ }
+ }
+
+ long getCpuTimeForPid(int pid) {
+ synchronized (mProcessCpuTracker) {
+ return mProcessCpuTracker.getCpuTimeForPid(pid);
+ }
+ }
+
+ List<ProcessCpuTracker.Stats> getCpuStats(Predicate<ProcessCpuTracker.Stats> predicate) {
+ synchronized (mProcessCpuTracker) {
+ return mProcessCpuTracker.getStats(st -> predicate.test(st));
+ }
+ }
+
+ void forAllCpuStats(Consumer<ProcessCpuTracker.Stats> consumer) {
+ synchronized (mProcessCpuTracker) {
+ final int numOfStats = mProcessCpuTracker.countStats();
+ for (int i = 0; i < numOfStats; i++) {
+ consumer.accept(mProcessCpuTracker.getStats(i));
+ }
+ }
+ }
+
+ private class ProcessCpuThread extends Thread {
+ ProcessCpuThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void run() {
+ synchronized (mProcessCpuTracker) {
+ mProcessCpuInitLatch.countDown();
+ mProcessCpuTracker.init();
+ }
+ while (true) {
+ try {
+ try {
+ synchronized (this) {
+ final long now = SystemClock.uptimeMillis();
+ long nextCpuDelay = (mLastCpuTime.get() + MONITOR_CPU_MAX_TIME) - now;
+ long nextWriteDelay = (mLastWriteTime + BATTERY_STATS_TIME) - now;
+ //Slog.i(TAG, "Cpu delay=" + nextCpuDelay
+ // + ", write delay=" + nextWriteDelay);
+ if (nextWriteDelay < nextCpuDelay) {
+ nextCpuDelay = nextWriteDelay;
+ }
+ if (nextCpuDelay > 0) {
+ mProcessCpuMutexFree.set(true);
+ this.wait(nextCpuDelay);
+ }
+ }
+ } catch (InterruptedException e) {
+ }
+ updateCpuStatsNow();
+ } catch (Exception e) {
+ Slog.e(TAG, "Unexpected exception collecting process stats", e);
+ }
+ }
+ }
+ }
+
+ class CpuBinder extends Binder {
+ private final PriorityDump.PriorityDumper mPriorityDumper =
+ new PriorityDump.PriorityDumper() {
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
+ boolean asProto) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mService.mContext, "cpuinfo", pw)) {
+ return;
+ }
+ synchronized (mProcessCpuTracker) {
+ if (asProto) {
+ mProcessCpuTracker.dumpProto(fd);
+ return;
+ }
+ pw.print(mProcessCpuTracker.printCurrentLoad());
+ pw.print(mProcessCpuTracker.printCurrentState(
+ SystemClock.uptimeMillis()));
+ }
+ }
+ };
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
+ }
+ }
+
+ void setCpuInfoService() {
+ if (MONITOR_CPU_USAGE) {
+ ServiceManager.addService("cpuinfo", new CpuBinder(),
+ /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
+ }
+ }
+
+ AppProfiler(ActivityManagerService service, Looper bgLooper, LowMemDetector detector) {
+ mService = service;
+ mBgHandler = new BgHandler(bgLooper);
+ mLowMemDetector = detector;
+ mProcessCpuThread = new ProcessCpuThread("CpuTracker");
+ }
+
+ void retrieveSettings() {
+ final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ActivityThread.currentApplication().getMainExecutor(),
+ mPssDelayConfigListener);
+ mPssDeferralTime = pssDeferralMs;
+ }
+
+ void onActivityManagerInternalAdded() {
+ mProcessCpuThread.start();
+ // Wait for the synchronized block started in mProcessCpuThread,
+ // so that any other access to mProcessCpuTracker from main thread
+ // will be blocked during mProcessCpuTracker initialization.
+ try {
+ mProcessCpuInitLatch.await();
+ } catch (InterruptedException e) {
+ Slog.wtf(TAG, "Interrupted wait during start", e);
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Interrupted wait during start");
+ }
+ }
+
+ void onActivityLaunched() {
+ // This is safe to force to the head of the queue because it relies only
+ // on refcounting to track begin/end of deferrals, not on actual
+ // message ordering. We don't care *what* activity is being
+ // launched; only that we're doing so.
+ if (mPssDeferralTime > 0) {
+ final Message msg = mBgHandler.obtainMessage(BgHandler.DEFER_PSS_MSG);
+ mBgHandler.sendMessageAtFrontOfQueue(msg);
+ }
+ }
+
+ @GuardedBy("mService")
+ ProfilerInfo setupProfilerInfoLocked(@NonNull IApplicationThread thread, ProcessRecord app,
+ ActiveInstrumentation instr) throws IOException, RemoteException {
+ ProfilerInfo profilerInfo = null;
+ String preBindAgent = null;
+ final String processName = app.processName;
+ if (mProfileData.getProfileApp() != null
+ && mProfileData.getProfileApp().equals(processName)) {
+ mProfileData.setProfileProc(app);
+ if (mProfileData.getProfilerInfo() != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
+ || mProfileData.getProfilerInfo().attachAgentDuringBind;
+ profilerInfo = needsInfo
+ ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
+ if (mProfileData.getProfilerInfo().agent != null) {
+ preBindAgent = mProfileData.getProfilerInfo().agent;
+ }
+ }
+ } else if (instr != null && instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
+ null, false);
+ }
+ if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+ // We need to do a debuggable check here. See setAgentApp for why the check is
+ // postponed to here.
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ String agent = mAppAgentMap.get(processName);
+ // Do not overwrite already requested agent.
+ if (profilerInfo == null) {
+ profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+ mAppAgentMap.get(processName), true);
+ } else if (profilerInfo.agent == null) {
+ profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+ }
+ }
+ }
+
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ profilerInfo.profileFd = profilerInfo.profileFd.dup();
+ if (TextUtils.equals(mProfileData.getProfileApp(), processName)
+ && mProfileData.getProfilerInfo() != null) {
+ clearProfilerLocked();
+ }
+ }
+
+ // Check if this is a secondary process that should be incorporated into some
+ // currently active instrumentation. (Note we do this AFTER all of the profiling
+ // stuff above because profiling can currently happen only in the primary
+ // instrumentation process.)
+ if (mService.mActiveInstrumentation.size() > 0 && instr == null) {
+ for (int i = mService.mActiveInstrumentation.size() - 1;
+ i >= 0 && app.getActiveInstrumentation() == null; i--) {
+ ActiveInstrumentation aInstr = mService.mActiveInstrumentation.get(i);
+ if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
+ if (aInstr.mTargetProcesses.length == 0) {
+ // This is the wildcard mode, where every process brought up for
+ // the target instrumentation should be included.
+ if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
+ app.setActiveInstrumentation(aInstr);
+ aInstr.mRunningProcesses.add(app);
+ }
+ } else {
+ for (String proc : aInstr.mTargetProcesses) {
+ if (proc.equals(app.processName)) {
+ app.setActiveInstrumentation(aInstr);
+ aInstr.mRunningProcesses.add(app);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If we were asked to attach an agent on startup, do so now, before we're binding
+ // application code.
+ if (preBindAgent != null) {
+ thread.attachAgent(preBindAgent);
+ }
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ thread.attachStartupAgents(app.info.dataDir);
+ }
+ return profilerInfo;
+ }
+
+ @GuardedBy("mService")
+ void onCleanupApplicationRecordLocked(ProcessRecord app) {
+ mPendingPssProcesses.remove(app);
+ abortNextPssTime(app.procStateMemTracker);
+ }
+
+ @GuardedBy("mService")
+ void onAppDiedLocked(ProcessRecord app) {
+ if (mProfileData.getProfileProc() == app) {
+ clearProfilerLocked();
+ }
+ }
+
+ @GuardedBy("mService")
+ boolean dumpMemWatchProcessesLocked(PrintWriter pw, boolean needSep) {
+ if (mMemWatchProcesses.getMap().size() > 0) {
+ pw.println(" Mem watch processes:");
+ final ArrayMap<String, SparseArray<Pair<Long, String>>> procs =
+ mMemWatchProcesses.getMap();
+ for (int i = procs.size() - 1; i >= 0; i--) {
+ final String proc = procs.keyAt(i);
+ final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+ for (int j = uids.size() - 1; j >= 0; j--) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(" ").append(proc).append('/');
+ UserHandle.formatUid(sb, uids.keyAt(j));
+ Pair<Long, String> val = uids.valueAt(j);
+ sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
+ if (val.second != null) {
+ sb.append(", report to ").append(val.second);
+ }
+ pw.println(sb.toString());
+ }
+ }
+ pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
+ pw.print(" mMemWatchDumpUri="); pw.println(mMemWatchDumpUri);
+ pw.print(" mMemWatchDumpPid="); pw.println(mMemWatchDumpPid);
+ pw.print(" mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
+ pw.print(" mMemWatchIsUserInitiated="); pw.println(mMemWatchIsUserInitiated);
+ }
+ return needSep;
+ }
+
+ @GuardedBy("mService")
+ boolean dumpProfileDataLocked(PrintWriter pw, String dumpPackage, boolean needSep) {
+ if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
+ || (mProfileData.getProfilerInfo() != null
+ && (mProfileData.getProfilerInfo().profileFile != null
+ || mProfileData.getProfilerInfo().profileFd != null))) {
+ if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mProfileApp=" + mProfileData.getProfileApp()
+ + " mProfileProc=" + mProfileData.getProfileProc());
+ if (mProfileData.getProfilerInfo() != null) {
+ pw.println(" mProfileFile=" + mProfileData.getProfilerInfo().profileFile
+ + " mProfileFd=" + mProfileData.getProfilerInfo().profileFd);
+ pw.println(" mSamplingInterval="
+ + mProfileData.getProfilerInfo().samplingInterval
+ + " mAutoStopProfiler="
+ + mProfileData.getProfilerInfo().autoStopProfiler
+ + " mStreamingOutput="
+ + mProfileData.getProfilerInfo().streamingOutput);
+ pw.println(" mProfileType=" + mProfileType);
+ }
+ }
+ }
+ return needSep;
+ }
+
+ @GuardedBy("mService")
+ void dumpLastMemoryLevelLocked(PrintWriter pw) {
+ switch (mLastMemoryLevel) {
+ case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
+ pw.println("normal)");
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+ pw.println("moderate)");
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ pw.println("low)");
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ pw.println("critical)");
+ break;
+ default:
+ pw.print(mLastMemoryLevel);
+ pw.println(")");
+ break;
+ }
+ }
+
+ @GuardedBy("mService")
+ void dumpMemoryLevelsLocked(PrintWriter pw) {
+ pw.println(" mAllowLowerMemLevel=" + mAllowLowerMemLevel
+ + " mLastMemoryLevel=" + mLastMemoryLevel
+ + " mLastNumProcesses=" + mLastNumProcesses);
+ }
+
+ @GuardedBy("mService")
+ void writeMemWatchProcessToProtoLocked(ProtoOutputStream proto) {
+ if (mMemWatchProcesses.getMap().size() > 0) {
+ final long token = proto.start(
+ ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
+ ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
+ for (int i = 0; i < procs.size(); i++) {
+ final String proc = procs.keyAt(i);
+ final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+ final long ptoken = proto.start(
+ ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME,
+ proc);
+ for (int j = uids.size() - 1; j >= 0; j--) {
+ final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto
+ .MemWatchProcess.Process.MEM_STATS);
+ Pair<Long, String> val = uids.valueAt(j);
+ proto.write(ActivityManagerServiceDumpProcessesProto
+ .MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
+ proto.write(ActivityManagerServiceDumpProcessesProto
+ .MemWatchProcess.Process.MemStats.SIZE,
+ DebugUtils.sizeValueToString(val.first, new StringBuilder()));
+ proto.write(ActivityManagerServiceDumpProcessesProto
+ .MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
+ proto.end(utoken);
+ }
+ proto.end(ptoken);
+ }
+
+ final long dtoken = proto.start(
+ ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME,
+ mMemWatchDumpProcName);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.URI,
+ mMemWatchDumpUri.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID,
+ mMemWatchDumpPid);
+ proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID,
+ mMemWatchDumpUid);
+ proto.write(
+ ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.IS_USER_INITIATED,
+ mMemWatchIsUserInitiated);
+ proto.end(dtoken);
+
+ proto.end(token);
+ }
+ }
+
+ @GuardedBy("mService")
+ void writeProfileDataToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
+ if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
+ || (mProfileData.getProfilerInfo() != null
+ && (mProfileData.getProfilerInfo().profileFile != null
+ || mProfileData.getProfilerInfo().profileFd != null))) {
+ if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
+ final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME,
+ mProfileData.getProfileApp());
+ mProfileData.getProfileProc().dumpDebug(proto,
+ ActivityManagerServiceDumpProcessesProto.Profile.PROC);
+ if (mProfileData.getProfilerInfo() != null) {
+ mProfileData.getProfilerInfo().dumpDebug(proto,
+ ActivityManagerServiceDumpProcessesProto.Profile.INFO);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE,
+ mProfileType);
+ }
+ proto.end(token);
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void writeMemoryLevelsToProtoLocked(ProtoOutputStream proto) {
+ proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL,
+ mAllowLowerMemLevel);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+ }
+
+ void printCurrentCpuState(StringBuilder report, long time) {
+ synchronized (mProcessCpuTracker) {
+ report.append(mProcessCpuTracker.printCurrentState(time));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6948f9061038..93105daa6c5d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -964,7 +964,7 @@ public final class BroadcastQueue {
+ mParallelBroadcasts.size() + " parallel broadcasts; "
+ mDispatcher.describeStateLocked());
- mService.updateCpuStats();
+ mService.updateCpuStatsLocked();
if (fromMsg) {
mBroadcastsScheduled = false;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index fc330313373e..c313eb5760eb 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -66,9 +66,9 @@ import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ;
import static com.android.server.am.ActivityManagerService.TAG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerService.TAG_PSS;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST;
+import static com.android.server.am.AppProfiler.TAG_PSS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import android.app.ActivityManager;
@@ -83,7 +83,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
-import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManagerInternal;
@@ -102,7 +101,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.compat.IPlatformCompat;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -819,7 +817,7 @@ public final class OomAdjuster {
}
if (allChanged) {
- mService.requestPssAllProcsLocked(now, false,
+ mService.mAppProfiler.requestPssAllProcsLocked(now, false,
mService.mProcessStats.isMemFactorLowered());
}
@@ -1075,7 +1073,7 @@ public final class OomAdjuster {
mProcessList.incrementProcStateSeqAndNotifyAppsLocked(activeUids);
- return mService.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+ return mService.mAppProfiler.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
}
private void updateAppUidRecLocked(ProcessRecord app) {
@@ -2176,7 +2174,7 @@ public final class OomAdjuster {
// normally be a B service, but if we are low on RAM and it
// is large we want to force it down since we would prefer to
// keep launcher over it.
- if (mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ if (!mService.mAppProfiler.isLastMemoryLevelNormal()
&& app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
app.serviceHighRam = true;
app.serviceb = true;
@@ -2493,45 +2491,16 @@ public final class OomAdjuster {
}
if (app.setProcState == PROCESS_STATE_NONEXISTENT
|| ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
- if (false && mService.mTestPssMode
- && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
- // Experimental code to more aggressively collect pss while
- // running test... the problem is that this tends to collect
- // the data right when a process is transitioning between process
- // states, which will tend to give noisy data.
- long start = SystemClock.uptimeMillis();
- long startTime = SystemClock.currentThreadTimeMillis();
- long pss = Debug.getPss(app.pid, mTmpLong, null);
- long endTime = SystemClock.currentThreadTimeMillis();
- mService.recordPssSampleLocked(app, app.getCurProcState(), pss,
- mTmpLong[0], mTmpLong[1], mTmpLong[2],
- ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
- mService.mPendingPssProcesses.remove(app);
- Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
- + " to " + app.getCurProcState() + ": "
- + (SystemClock.uptimeMillis()-start) + "ms");
- }
app.lastStateTime = now;
- app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
- app.procStateMemTracker, mService.mTestPssMode,
- mService.mAtmInternal.isSleeping(), now);
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
- + ProcessList.makeProcStateString(app.setProcState) + " to "
- + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
- + (app.nextPssTime-now) + ": " + app);
- } else {
- if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
- && now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
- mService.mTestPssMode)))) {
- if (mService.requestPssLocked(app, app.setProcState)) {
- app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
- app.procStateMemTracker, mService.mTestPssMode,
- mService.mAtmInternal.isSleeping(), now);
- }
- } else if (false && DEBUG_PSS) {
- Slog.d(TAG_PSS,
- "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
+ mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
+ if (DEBUG_PSS) {
+ Slog.d(TAG_PSS, "Process state change from "
+ + ProcessList.makeProcStateString(app.setProcState) + " to "
+ + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+ + (app.nextPssTime - now) + ": " + app);
}
+ } else {
+ mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, false);
}
if (app.setProcState != app.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 87898d80bd46..ced2f0f5ebc4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -48,8 +48,8 @@ import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
import static com.android.server.am.ActivityManagerService.TAG_NETWORK;
import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
-import static com.android.server.am.ActivityManagerService.TAG_PSS;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
+import static com.android.server.am.AppProfiler.TAG_PSS;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -1323,19 +1323,6 @@ public final class ProcessList {
return test ? PSS_TEST_MIN_TIME_FROM_STATE_CHANGE : PSS_MIN_TIME_FROM_STATE_CHANGE;
}
- public static void commitNextPssTime(ProcStateMemTracker tracker) {
- if (tracker.mPendingMemState >= 0) {
- tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
- tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
- tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
- tracker.mPendingMemState = -1;
- }
- }
-
- public static void abortNextPssTime(ProcStateMemTracker tracker) {
- tracker.mPendingMemState = -1;
- }
-
public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
boolean sleeping, long now) {
boolean first;
@@ -1547,7 +1534,7 @@ public final class ProcessList {
ApplicationExitInfo.SUBREASON_LARGE_CACHED,
true);
} else if (proc != null && !keepIfLarge
- && mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ && !mService.mAppProfiler.isLastMemoryLevelNormal()
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
.lastCachedPss);
@@ -1772,7 +1759,7 @@ public final class ProcessList {
mService.mProcessesOnHold.remove(app);
checkSlow(startTime, "startProcess: starting to update cpu stats");
- mService.updateCpuStats();
+ mService.updateCpuStatsLocked();
checkSlow(startTime, "startProcess: done updating cpu stats");
try {
@@ -2422,7 +2409,8 @@ public final class ProcessList {
ProcessList.killProcessGroup(app.uid, app.pid);
checkSlow(startTime, "startProcess: done killing old proc");
- if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ if (!app.killed
+ || mService.mAppProfiler.isLastMemoryLevelNormal()
|| app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
|| app.lastCachedPss < getCachedRestoreThresholdKb()) {
// Throw a wtf if it's not killed, or killed but not because the system was in
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 14bc6cbed7d7..f1e12c55df18 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1422,12 +1422,7 @@ class ProcessRecord implements WindowProcessListener {
@Override
public void clearProfilerIfNeeded() {
synchronized (mService) {
- if (mService.mProfileData.getProfileProc() == null
- || mService.mProfileData.getProfilerInfo() == null
- || mService.mProfileData.getProfileProc() != this) {
- return;
- }
- mService.clearProfilerLocked();
+ mService.mAppProfiler.clearProfilerLocked();
}
}
@@ -1483,7 +1478,7 @@ class ProcessRecord implements WindowProcessListener {
*/
@Override
public long getCpuTime() {
- return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
+ return mService.mAppProfiler.getCpuTimeForPid(pid);
}
@Override
@@ -1492,7 +1487,7 @@ class ProcessRecord implements WindowProcessListener {
synchronized (mService) {
waitingToKill = null;
if (setProfileProc) {
- mService.mProfileData.setProfileProc(this);
+ mService.mAppProfiler.setProfileProcLocked(this);
}
if (packageName != null) {
addPackage(packageName, versionCode, mService.mProcessStats);
@@ -1566,7 +1561,7 @@ class ProcessRecord implements WindowProcessListener {
/** Non-private access is for tests only. */
@VisibleForTesting
boolean isMonitorCpuUsage() {
- return mService.MONITOR_CPU_USAGE;
+ return mService.mAppProfiler.MONITOR_CPU_USAGE;
}
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
@@ -1701,9 +1696,7 @@ class ProcessRecord implements WindowProcessListener {
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
- synchronized (mService.mProcessCpuTracker) {
- report.append(mService.mProcessCpuTracker.printCurrentState(anrTime));
- }
+ mService.mAppProfiler.printCurrentCpuState(report, anrTime);
info.append(processCpuTracker.printCurrentLoad());
info.append(report);
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 4e8c386a3c66..d925defdbc73 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -314,7 +314,8 @@ public final class ProcessStatsService extends IProcessStats.Stub {
private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
mAm.mHandler.post(() -> {
synchronized (mAm) {
- mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered);
+ mAm.mAppProfiler.requestPssAllProcsLocked(
+ SystemClock.uptimeMillis(), always, memLowered);
}
});
}
@@ -1056,17 +1057,13 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
}
} else if ("--start-testing".equals(arg)) {
- synchronized (mAm) {
- mAm.setTestPssMode(true);
- pw.println("Started high frequency sampling.");
- quit = true;
- }
+ mAm.mAppProfiler.setTestPssMode(true);
+ pw.println("Started high frequency sampling.");
+ quit = true;
} else if ("--stop-testing".equals(arg)) {
- synchronized (mAm) {
- mAm.setTestPssMode(false);
- pw.println("Stopped high frequency sampling.");
- quit = true;
- }
+ mAm.mAppProfiler.setTestPssMode(false);
+ pw.println("Stopped high frequency sampling.");
+ quit = true;
} else if ("--pretend-screen-on".equals(arg)) {
synchronized (mLock) {
mInjectedScreenState = true;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index b56b09d707af..06f44b1bd388 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -39,6 +39,7 @@ import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
import static android.app.AppOpsManager.OpEventProxyInfo;
import static android.app.AppOpsManager.RestrictionBypass;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
@@ -3428,7 +3429,19 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
+ return AppOpsManager.MODE_IGNORED;
+ }
+
+ // As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution
+ // purposes and not as a check, also make sure that the caller is allowed to access
+ // the data gated by OP_RECORD_AUDIO.
+ //
+ // TODO: Revert this change before Android 12.
+ if (code == OP_RECORD_AUDIO_HOTWORD) {
+ int result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
+ if (result != AppOpsManager.MODE_ALLOWED) {
+ return result;
+ }
}
RestrictionBypass bypass;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index d2c35feccc11..1613d3bb4e8a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -282,8 +282,10 @@ class Face10 implements IHwBinder.DeathRecipient {
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
final boolean supportsSelfIllumination = context.getResources()
.getBoolean(R.bool.config_faceAuthSupportsSelfIllumination);
+ final int maxTemplatesAllowed = context.getResources()
+ .getInteger(R.integer.config_faceMaxTemplatesPerUser);
mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */,
- supportsSelfIllumination);
+ supportsSelfIllumination, maxTemplatesAllowed);
mContext = context;
mSensorId = sensorId;
mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
index 3754bd748781..7e62329a168a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -45,6 +45,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.R;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
@@ -338,8 +339,10 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
: FingerprintSensorProperties.TYPE_REAR;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
+ final int maxTemplatesAllowed = mContext.getResources()
+ .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType,
- resetLockoutRequiresHardwareAuthToken);
+ resetLockoutRequiresHardwareAuthToken, maxTemplatesAllowed);
}
static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
index 1a6ad46fce67..7cab2b889eaa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
@@ -35,6 +35,7 @@ import android.provider.Settings;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import com.android.internal.R;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitor;
@@ -408,8 +409,11 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
mHandler = handler;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
+ final int maxTemplatesAllowed = mContext.getResources()
+ .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
mSensorProperties = new FingerprintSensorProperties(sensorId,
- FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken);
+ FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken,
+ maxTemplatesAllowed);
mMockHalResultController = controller;
mUserHasTrust = new SparseBooleanArray();
mTrustManager = context.getSystemService(TrustManager.class);
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index d9b5b6d41c11..f15e22f92cad 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -101,7 +101,7 @@ class MediaSessionStack {
if (mMediaButtonSession == record) {
// When the media button session is removed, nullify the media button session and do not
// search for the alternative media session within the app. It's because the alternative
- // media session might be a dummy which isn't able to handle the media key events.
+ // media session might be a fake which isn't able to handle the media key events.
// TODO(b/154456172): Make this decision unaltered by non-media app's playback.
updateMediaButtonSession(null);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index bae02ac75f0a..cff4f07fa9d8 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -469,6 +469,69 @@ public final class NotificationRecord {
pw.println(prefix + "key=" + getSbn().getKey());
pw.println(prefix + "seen=" + mStats.hasSeen());
pw.println(prefix + "groupKey=" + getGroupKey());
+ pw.println(prefix + "notification=");
+ dumpNotification(pw, prefix + prefix, notification, redact);
+ pw.println(prefix + "publicNotification=");
+ dumpNotification(pw, prefix + prefix, notification.publicVersion, redact);
+ pw.println(prefix + "stats=" + stats.toString());
+ pw.println(prefix + "mContactAffinity=" + mContactAffinity);
+ pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
+ pw.println(prefix + "mPackagePriority=" + mPackagePriority);
+ pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
+ pw.println(prefix + "mSystemImportance="
+ + NotificationListenerService.Ranking.importanceToString(mSystemImportance));
+ pw.println(prefix + "mAsstImportance="
+ + NotificationListenerService.Ranking.importanceToString(mAssistantImportance));
+ pw.println(prefix + "mImportance="
+ + NotificationListenerService.Ranking.importanceToString(mImportance));
+ pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation());
+ pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
+ pw.println(prefix + "mIntercept=" + mIntercept);
+ pw.println(prefix + "mHidden==" + mHidden);
+ pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
+ pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
+ pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
+ pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
+ pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
+ pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
+ pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
+ if (mPreChannelsNotification) {
+ pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
+ notification.defaults, notification.flags));
+ pw.println(prefix + "n.sound=" + notification.sound);
+ pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
+ pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
+ pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
+ notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
+ pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
+ }
+ pw.println(prefix + "mSound= " + mSound);
+ pw.println(prefix + "mVibration= " + mVibration);
+ pw.println(prefix + "mAttributes= " + mAttributes);
+ pw.println(prefix + "mLight= " + mLight);
+ pw.println(prefix + "mShowBadge=" + mShowBadge);
+ pw.println(prefix + "mColorized=" + notification.isColorized());
+ pw.println(prefix + "mAllowBubble=" + mAllowBubble);
+ pw.println(prefix + "isBubble=" + notification.isBubbleNotification());
+ pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
+ pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
+ if (getPeopleOverride() != null) {
+ pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
+ }
+ if (getSnoozeCriteria() != null) {
+ pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
+ }
+ pw.println(prefix + "mAdjustments=" + mAdjustments);
+ pw.println(prefix + "shortcut=" + notification.getShortcutId()
+ + " found valid? " + (mShortcutInfo != null));
+ }
+
+ private void dumpNotification(PrintWriter pw, String prefix, Notification notification,
+ boolean redact) {
+ if (notification == null) {
+ pw.println(prefix + "None");
+ return;
+ }
pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
pw.println(prefix + "contentIntent=" + notification.contentIntent);
pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
@@ -545,57 +608,6 @@ public final class NotificationRecord {
}
pw.println(prefix + "}");
}
- pw.println(prefix + "stats=" + stats.toString());
- pw.println(prefix + "mContactAffinity=" + mContactAffinity);
- pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
- pw.println(prefix + "mPackagePriority=" + mPackagePriority);
- pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
- pw.println(prefix + "mSystemImportance="
- + NotificationListenerService.Ranking.importanceToString(mSystemImportance));
- pw.println(prefix + "mAsstImportance="
- + NotificationListenerService.Ranking.importanceToString(mAssistantImportance));
- pw.println(prefix + "mImportance="
- + NotificationListenerService.Ranking.importanceToString(mImportance));
- pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation());
- pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
- pw.println(prefix + "mIntercept=" + mIntercept);
- pw.println(prefix + "mHidden==" + mHidden);
- pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
- pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
- pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
- pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
- pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
- pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
- pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
- if (mPreChannelsNotification) {
- pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
- notification.defaults, notification.flags));
- pw.println(prefix + "n.sound=" + notification.sound);
- pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
- pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
- pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
- notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
- pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
- }
- pw.println(prefix + "mSound= " + mSound);
- pw.println(prefix + "mVibration= " + mVibration);
- pw.println(prefix + "mAttributes= " + mAttributes);
- pw.println(prefix + "mLight= " + mLight);
- pw.println(prefix + "mShowBadge=" + mShowBadge);
- pw.println(prefix + "mColorized=" + notification.isColorized());
- pw.println(prefix + "mAllowBubble=" + mAllowBubble);
- pw.println(prefix + "isBubble=" + notification.isBubbleNotification());
- pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
- pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
- if (getPeopleOverride() != null) {
- pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
- }
- if (getSnoozeCriteria() != null) {
- pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
- }
- pw.println(prefix + "mAdjustments=" + mAdjustments);
- pw.println(prefix + "shortcut=" + notification.getShortcutId()
- + " found valid? " + (mShortcutInfo != null));
}
@Override
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5417275bc8f1..13cd6e547629 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -417,7 +417,9 @@ public class ZenModeHelper {
if (mConfig == null) return;
newConfig = mConfig.copy();
- setAutomaticZenRuleStateLocked(newConfig, newConfig.automaticRules.get(id), condition);
+ ArrayList<ZenRule> rules = new ArrayList<>();
+ rules.add(newConfig.automaticRules.get(id));
+ setAutomaticZenRuleStateLocked(newConfig, rules, condition);
}
}
@@ -428,31 +430,34 @@ public class ZenModeHelper {
newConfig = mConfig.copy();
setAutomaticZenRuleStateLocked(newConfig,
- findMatchingRule(newConfig, ruleDefinition, condition),
+ findMatchingRules(newConfig, ruleDefinition, condition),
condition);
}
}
- private void setAutomaticZenRuleStateLocked(ZenModeConfig config, ZenRule rule,
+ private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition) {
- if (rule == null) return;
+ if (rules == null || rules.isEmpty()) return;
- rule.condition = condition;
- updateSnoozing(rule);
- setConfigLocked(config, rule.component, "conditionChanged");
+ for (ZenRule rule : rules) {
+ rule.condition = condition;
+ updateSnoozing(rule);
+ setConfigLocked(config, rule.component, "conditionChanged");
+ }
}
- private ZenRule findMatchingRule(ZenModeConfig config, Uri id, Condition condition) {
+ private List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id, Condition condition) {
+ List<ZenRule> matchingRules= new ArrayList<>();
if (ruleMatches(id, condition, config.manualRule)) {
- return config.manualRule;
+ matchingRules.add(config.manualRule);
} else {
for (ZenRule automaticRule : config.automaticRules.values()) {
if (ruleMatches(id, condition, automaticRule)) {
- return automaticRule;
+ matchingRules.add(automaticRule);
}
}
}
- return null;
+ return matchingRules;
}
private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fcf5d9691d99..69e8e65166ac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1848,7 +1848,7 @@ public class PackageManagerService extends IPackageManager.Stub
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_LIST);
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writePackageListLPr(msg.arg1);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -3519,7 +3519,7 @@ public class PackageManagerService extends IPackageManager.Stub
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
- mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
+ mPermissionManager.readStateFromPackageSettingsTEMP();
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
@@ -21826,7 +21826,7 @@ public class PackageManagerService extends IPackageManager.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
@@ -23706,7 +23706,7 @@ public class PackageManagerService extends IPackageManager.Stub
mDirtyUsers.remove(userId);
mUserNeedsBadging.delete(userId);
mPermissionManager.onUserRemoved(userId);
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
mInstantAppRegistry.onUserRemovedLPw(userId);
@@ -23807,9 +23807,9 @@ public class PackageManagerService extends IPackageManager.Stub
boolean readPermissionStateForUser(@UserIdInt int userId) {
synchronized (mPackages) {
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.readPermissionStateForUserSyncLPr(userId);
- mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
+ mPermissionManager.readStateFromPackageSettingsTEMP();
return mPmInternal.isPermissionUpgradeNeeded(userId);
}
}
@@ -25823,12 +25823,12 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Temporary method that wraps mSettings.writeLPr() and calls
- * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand.
+ * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand.
*
* TODO(zhanghai): This should be removed once we finish migration of permission storage.
*/
private void writeSettingsLPrTEMP() {
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writeLPr();
}
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 962638b4f63c..865b8a1e97eb 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -36,6 +36,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSettingBase;
@@ -139,6 +140,10 @@ public final class BasePermission {
this.perm = perm;
}
+ public boolean hasGids() {
+ return !ArrayUtils.isEmpty(gids);
+ }
+
public int[] computeGids(int userId) {
if (perUser) {
final int[] userGids = new int[gids.length];
@@ -419,9 +424,9 @@ public final class BasePermission {
}
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
- PermissionsState permsState) {
+ UidPermissionState uidState) {
int index = pkg.getRequestedPermissions().indexOf(name);
- if (!permsState.hasRequestedPermission(name) && index == -1) {
+ if (!uidState.hasRequestedPermission(name) && index == -1) {
throw new SecurityException("Package " + pkg.getPackageName()
+ " has not requested permission " + name);
}
diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
new file mode 100644
index 000000000000..b9456acfced5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 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.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Permission state for this device.
+ */
+public final class DevicePermissionState {
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public DevicePermissionState(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ @Nullable
+ public UserPermissionState getUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mUserStates.get(userId);
+ }
+ }
+
+ @NonNull
+ public UserPermissionState getOrCreateUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ UserPermissionState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserPermissionState(mLock);
+ mUserStates.put(userId, userState);
+ }
+ return userState;
+ }
+ }
+
+ public void removeUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mUserStates.delete(userId);
+ }
+ }
+
+ public int[] getUserIds() {
+ synchronized (mLock) {
+ final int userStatesSize = mUserStates.size();
+ final int[] userIds = new int[userStatesSize];
+ for (int i = 0; i < userStatesSize; i++) {
+ final int userId = mUserStates.keyAt(i);
+ userIds[i] = userId;
+ }
+ return userIds;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1cfc5b135cfa..75e5944b3cb4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -148,12 +148,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.Default
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
-import libcore.util.EmptyArray;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -226,8 +223,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
/** Internal connection to the user manager */
private final UserManagerInternal mUserManagerInt;
- /** Maps from App ID to PermissionsState */
- private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>();
+ @NonNull
+ private final DevicePermissionState mState;
/** Permission controller: User space permission management */
private PermissionControllerManager mPermissionControllerManager;
@@ -395,6 +392,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mSettings = new PermissionSettings(mLock);
+ mState = new DevicePermissionState(mLock);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mHandlerThread = new ServiceThread(TAG,
@@ -681,12 +679,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
return 0;
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return 0;
}
- return permissionsState.getPermissionFlags(permName, userId);
+ return uidState.getPermissionFlags(permName);
}
@Override
@@ -788,14 +786,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return;
}
- final boolean hadState =
- permissionsState.getRuntimePermissionState(permName, userId) != null;
+ final boolean hadState = uidState.getPermissionState(permName) != null;
if (!hadState) {
boolean isRequested = false;
// Fast path, the current package has requested the permission.
@@ -822,20 +819,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
final boolean permissionUpdated =
- permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
+ uidState.updatePermissionFlags(bp, flagMask, flagValues);
if (permissionUpdated && bp.isRuntime()) {
notifyRuntimePermissionStateChanged(packageName, userId);
}
if (permissionUpdated && callback != null) {
// Install and runtime permissions are stored in different places,
// so figure out what permission changed and persist the change.
- if (permissionsState.getInstallPermissionState(permName) != null) {
+ if (!bp.isRuntime()) {
int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
callback.onInstallPermissionUpdatedNotifyListener(userUid);
- } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
- || hadState) {
- callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false,
- pkg.getUid());
+ } else {
+ callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid());
}
}
}
@@ -868,13 +863,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final boolean[] changed = new boolean[1];
mPackageManagerInt.forEachPackage(pkg -> {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions(
- userId, effectiveFlagMask, effectiveFlagValues);
+ changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
+ effectiveFlagMask, effectiveFlagValues);
mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
});
@@ -926,19 +922,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
final int uid = UserHandle.getUid(userId, pkg.getUid());
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return PackageManager.PERMISSION_DENIED;
}
- if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
+ if (checkSinglePermissionInternal(uid, uidState, permissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
if (fullerPermissionName != null
- && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
+ && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
@@ -946,8 +943,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean checkSinglePermissionInternal(int uid,
- @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
- if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
+ @NonNull UidPermissionState uidState, @NonNull String permissionName) {
+ if (!uidState.hasPermission(permissionName)) {
return false;
}
@@ -1141,9 +1138,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return null;
}
@@ -1164,7 +1161,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int i = 0; i < permissionCount; i++) {
final String permissionName = pkg.getRequestedPermissions().get(i);
final int currentFlags =
- permissionsState.getPermissionFlags(permissionName, userId);
+ uidState.getPermissionFlags(permissionName);
if ((currentFlags & queryFlags) != 0) {
if (whitelistedPermissions == null) {
whitelistedPermissions = new ArrayList<>();
@@ -1453,13 +1450,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1472,7 +1470,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
- final int flags = permissionsState.getPermissionFlags(permName, userId);
+ final int flags = uidState.getPermissionFlags(permName);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
Log.e(TAG, "Cannot grant system fixed permission "
+ permName + " for package " + packageName);
@@ -1502,8 +1500,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
- if (permissionsState.grantInstallPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
+ // TODO(zhanghai): We are breaking the behavior above by making all permission state
+ // per-user. It isn't documented behavior and relatively rarely used anyway.
+ if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
if (callback != null) {
callback.onInstallPermissionGranted();
}
@@ -1521,7 +1520,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- final int result = permissionsState.grantRuntimePermission(bp, userId);
+ final int result = uidState.grantPermission(bp);
switch (result) {
case PERMISSION_OPERATION_FAILURE: {
return;
@@ -1617,13 +1616,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1634,7 +1634,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- final int flags = permissionsState.getPermissionFlags(permName, userId);
+ final int flags = uidState.getPermissionFlags(permName);
// Only the system may revoke SYSTEM_FIXED permissions.
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
&& UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
@@ -1649,8 +1649,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
- if (permissionsState.revokeInstallPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
+ // TODO(zhanghai): We are breaking the behavior above by making all permission state
+ // per-user. It isn't documented behavior and relatively rarely used anyway.
+ if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) {
if (callback != null) {
mDefaultPermissionCallback.onInstallPermissionRevoked();
}
@@ -1659,12 +1660,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Permission is already revoked, no need to do anything.
- if (!permissionsState.hasRuntimePermission(permName, userId)) {
+ if (!uidState.hasPermission(permName)) {
return;
}
- if (permissionsState.revokeRuntimePermission(bp, userId)
- == PERMISSION_OPERATION_FAILURE) {
+ if (uidState.revokePermission(bp) == PERMISSION_OPERATION_FAILURE) {
return;
}
@@ -2466,19 +2466,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) {
- final int appIdStatesSize = mAppIdStates.size();
- for (int i = 0; i < appIdStatesSize; i++) {
- PermissionsState permissionsState = mAppIdStates.valueAt(i);
- for (PermissionState permissionState
- : permissionsState.getRuntimePermissionStates(userId)) {
- BasePermission bp = mSettings.getPermission(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
+ mState.removeUserState(userId);
}
}
@@ -2489,19 +2477,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return Collections.emptySet();
}
- final PermissionsState permissionsState = getPermissionsState(ps);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(ps, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return Collections.emptySet();
}
if (!ps.getInstantApp(userId)) {
- return permissionsState.getPermissions(userId);
+ return uidState.getPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
// per-user, so we can only filter it here unless we make install permission state
// per-user as well.
- final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions(
- userId));
+ final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions());
instantPermissions.removeIf(permissionName -> {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
@@ -2533,12 +2520,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return null;
}
- final PermissionsState permissionsState = getPermissionsState(ps);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(ps, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return null;
}
- return permissionsState.computeGids(userId);
+ return uidState.computeGids(userId);
}
/**
@@ -2575,15 +2562,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return;
}
- final PermissionsState permissionsState = getOrCreatePermissionsState(ps);
final int[] userIds = getAllUserIds();
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
- for (int userId : userIds) {
- if (permissionsState.isMissing(userId)) {
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+ final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId());
+
+ if (uidState.isMissing()) {
Collection<String> requestedPermissions;
int targetSdkVersion;
if (!ps.isSharedUser()) {
@@ -2611,222 +2600,220 @@ public class PermissionManagerService extends IPermissionManager.Stub {
&& permission.isRuntime() && !permission.isRemoved()) {
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
- permissionsState.updatePermissionFlags(permission, userId,
+ uidState.updatePermissionFlags(permission,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
- permissionsState.updatePermissionFlags(permission, userId,
+ uidState.updatePermissionFlags(permission,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
- permissionsState.grantRuntimePermission(permission, userId);
+ uidState.grantPermission(permission);
}
}
}
- permissionsState.setMissing(false, userId);
+ uidState.setMissing(false);
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- }
- PermissionsState origPermissions = permissionsState;
+ UidPermissionState origState = uidState;
- boolean changedInstallPermission = false;
+ boolean changedInstallPermission = false;
- if (replace) {
- ps.setInstallPermissionsFixed(false);
- if (!ps.isSharedUser()) {
- origPermissions = new PermissionsState(permissionsState);
- permissionsState.reset();
- } else {
- // We need to know only about runtime permission changes since the
- // calling code always writes the install permissions state but
- // the runtime ones are written only if changed. The only cases of
- // changed runtime permissions here are promotion of an install to
- // runtime and revocation of a runtime from a shared user.
- synchronized (mLock) {
- updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
- ps.getSharedUser().getPackages(), permissionsState, userIds);
- if (!ArrayUtils.isEmpty(updatedUserIds)) {
- runtimePermissionsRevoked = true;
+ if (replace) {
+ userState.setInstallPermissionsFixed(ps.name, false);
+ if (!ps.isSharedUser()) {
+ origState = new UidPermissionState(uidState);
+ uidState.reset();
+ } else {
+ // We need to know only about runtime permission changes since the
+ // calling code always writes the install permissions state but
+ // the runtime ones are written only if changed. The only cases of
+ // changed runtime permissions here are promotion of an install to
+ // runtime and revocation of a runtime from a shared user.
+ synchronized (mLock) {
+ if (revokeUnusedSharedUserPermissionsLocked(
+ ps.getSharedUser().getPackages(), uidState)) {
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ runtimePermissionsRevoked = true;
+ }
}
}
}
- }
- permissionsState.setGlobalGids(mGlobalGids);
+ uidState.setGlobalGids(mGlobalGids);
- ArraySet<String> newImplicitPermissions = new ArraySet<>();
+ ArraySet<String> newImplicitPermissions = new ArraySet<>();
- final int N = pkg.getRequestedPermissions().size();
- for (int i = 0; i < N; i++) {
- final String permName = pkg.getRequestedPermissions().get(i);
- final BasePermission bp = mSettings.getPermission(permName);
- final boolean appSupportsRuntimePermissions =
- pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
- String upgradedActivityRecognitionPermission = null;
+ final int N = pkg.getRequestedPermissions().size();
+ for (int i = 0; i < N; i++) {
+ final String permName = pkg.getRequestedPermissions().get(i);
+ final BasePermission bp = mSettings.getPermission(permName);
+ final boolean appSupportsRuntimePermissions =
+ pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
+ String upgradedActivityRecognitionPermission = null;
- if (DEBUG_INSTALL && bp != null) {
- Log.i(TAG, "Package " + pkg.getPackageName()
- + " checking " + permName + ": " + bp);
- }
+ if (DEBUG_INSTALL && bp != null) {
+ Log.i(TAG, "Package " + pkg.getPackageName()
+ + " checking " + permName + ": " + bp);
+ }
- if (bp == null || getSourcePackageSetting(bp) == null) {
- if (packageOfInterest == null || packageOfInterest.equals(
- pkg.getPackageName())) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Unknown permission " + permName
- + " in package " + pkg.getPackageName());
+ if (bp == null || getSourcePackageSetting(bp) == null) {
+ if (packageOfInterest == null || packageOfInterest.equals(
+ pkg.getPackageName())) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Unknown permission " + permName
+ + " in package " + pkg.getPackageName());
+ }
}
+ continue;
}
- continue;
- }
- // Cache newImplicitPermissions before modifing permissionsState as for the shared
- // uids the original and new state are the same object
- if (!origPermissions.hasRequestedPermission(permName)
- && (pkg.getImplicitPermissions().contains(permName)
- || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
- if (pkg.getImplicitPermissions().contains(permName)) {
- // If permName is an implicit permission, try to auto-grant
- newImplicitPermissions.add(permName);
+ // Cache newImplicitPermissions before modifing permissionsState as for the shared
+ // uids the original and new state are the same object
+ if (!origState.hasRequestedPermission(permName)
+ && (pkg.getImplicitPermissions().contains(permName)
+ || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+ if (pkg.getImplicitPermissions().contains(permName)) {
+ // If permName is an implicit permission, try to auto-grant
+ newImplicitPermissions.add(permName);
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName());
- }
- } else {
- // Special case for Activity Recognition permission. Even if AR permission
- // is not an implicit permission we want to add it to the list (try to
- // auto-grant it) if the app was installed on a device before AR permission
- // was split, regardless of if the app now requests the new AR permission
- // or has updated its target SDK and AR is no longer implicit to it.
- // This is a compatibility workaround for apps when AR permission was
- // split in Q.
- final List<SplitPermissionInfoParcelable> permissionList =
- getSplitPermissions();
- int numSplitPerms = permissionList.size();
- for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
- String splitPermName = sp.getSplitPermission();
- if (sp.getNewPermissions().contains(permName)
- && origPermissions.hasInstallPermission(splitPermName)) {
- upgradedActivityRecognitionPermission = splitPermName;
- newImplicitPermissions.add(permName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName());
+ }
+ } else {
+ // Special case for Activity Recognition permission. Even if AR permission
+ // is not an implicit permission we want to add it to the list (try to
+ // auto-grant it) if the app was installed on a device before AR permission
+ // was split, regardless of if the app now requests the new AR permission
+ // or has updated its target SDK and AR is no longer implicit to it.
+ // This is a compatibility workaround for apps when AR permission was
+ // split in Q.
+ final List<SplitPermissionInfoParcelable> permissionList =
+ getSplitPermissions();
+ int numSplitPerms = permissionList.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
+ String splitPermName = sp.getSplitPermission();
+ if (sp.getNewPermissions().contains(permName)
+ && origState.hasInstallPermission(splitPermName)) {
+ upgradedActivityRecognitionPermission = splitPermName;
+ newImplicitPermissions.add(permName);
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for "
- + pkg.getPackageName());
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for "
+ + pkg.getPackageName());
+ }
+ break;
}
- break;
}
}
}
- }
-
- // TODO(b/140256621): The package instant app method has been removed
- // as part of work in b/135203078, so this has been commented out in the meantime
- // Limit ephemeral apps to ephemeral allowed permissions.
-// if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
-// if (DEBUG_PERMISSIONS) {
-// Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
-// + " for package " + pkg.getPackageName());
-// }
-// continue;
-// }
- if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
- if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying runtime-only permission " + bp.getName()
- + " for package " + pkg.getPackageName());
+ // TODO(b/140256621): The package instant app method has been removed
+ // as part of work in b/135203078, so this has been commented out in the meantime
+ // Limit ephemeral apps to ephemeral allowed permissions.
+ // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
+ // if (DEBUG_PERMISSIONS) {
+ // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+ // + " for package " + pkg.getPackageName());
+ // }
+ // continue;
+ // }
+
+ if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ + " for package " + pkg.getPackageName());
+ }
+ continue;
}
- continue;
- }
-
- final String perm = bp.getName();
- boolean allowedSig = false;
- int grant = GRANT_DENIED;
- // Keep track of app op permissions.
- if (bp.isAppOp()) {
- mSettings.addAppOpPackage(perm, pkg.getPackageName());
- }
+ final String perm = bp.getName();
+ boolean allowedSig = false;
+ int grant = GRANT_DENIED;
- if (bp.isNormal()) {
- // For all apps normal permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (bp.isRuntime()) {
- if (origPermissions.hasInstallPermission(bp.getName())
- || upgradedActivityRecognitionPermission != null) {
- // Before Q we represented some runtime permissions as install permissions,
- // in Q we cannot do this anymore. Hence upgrade them all.
- grant = GRANT_UPGRADE;
- } else {
- // For modern apps keep runtime permissions unchanged.
- grant = GRANT_RUNTIME;
+ // Keep track of app op permissions.
+ if (bp.isAppOp()) {
+ mSettings.addAppOpPackage(perm, pkg.getPackageName());
}
- } else if (bp.isSignature()) {
- // For all apps signature permissions are install time ones.
- allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origPermissions);
- if (allowedSig) {
+
+ if (bp.isNormal()) {
+ // For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
+ } else if (bp.isRuntime()) {
+ if (origState.hasInstallPermission(bp.getName())
+ || upgradedActivityRecognitionPermission != null) {
+ // Before Q we represented some runtime permissions as install permissions,
+ // in Q we cannot do this anymore. Hence upgrade them all.
+ grant = GRANT_UPGRADE;
+ } else {
+ // For modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ } else if (bp.isSignature()) {
+ // For all apps signature permissions are install time ones.
+ allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
}
- }
- if (grant != GRANT_DENIED) {
- if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) {
- // If this is an existing, non-system package, then
- // we can't add any new permissions to it. Runtime
- // permissions can be added any time - they ad dynamic.
- if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
- // Except... if this is a permission that was added
- // to the platform (note: need to only do this when
- // updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
- grant = GRANT_DENIED;
+ if (grant != GRANT_DENIED) {
+ if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)
+ && !bp.isRuntime()) {
+ // If this is an existing, non-system package, then
+ // we can't add any new permissions to it. Runtime
+ // permissions can be added any time - they ad dynamic.
+ if (!allowedSig && !origState.hasInstallPermission(perm)) {
+ // Except... if this is a permission that was added
+ // to the platform (note: need to only do this when
+ // updating the platform).
+ if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ grant = GRANT_DENIED;
+ }
}
}
}
- }
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Considering granting permission " + perm + " to package "
- + pkg.getPackageName());
- }
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Considering granting permission " + perm + " to package "
+ + pkg.getPackageName());
+ }
- synchronized (mLock) {
- if (grant != GRANT_DENIED) {
- switch (grant) {
- case GRANT_INSTALL: {
- // Revoke this as runtime permission to handle the case of
- // a runtime permission being downgraded to an install one.
- // Also in permission review mode we keep dangerous permissions
- // for legacy apps
- for (int userId : userIds) {
- if (origPermissions.getRuntimePermissionState(
- perm, userId) != null) {
+ synchronized (mLock) {
+ if (grant != GRANT_DENIED) {
+ switch (grant) {
+ case GRANT_INSTALL: {
+ // Revoke this as runtime permission to handle the case of
+ // a runtime permission being downgraded to an install one.
+ // Also in permission review mode we keep dangerous permissions
+ // for legacy apps
+ final PermissionState origPermissionState =
+ origState.getPermissionState(perm);
+ if (origPermissionState != null
+ && origPermissionState.isRuntime()) {
// Revoke the runtime permission and clear the flags.
- origPermissions.revokeRuntimePermission(bp, userId);
- origPermissions.updatePermissionFlags(bp, userId,
+ origState.revokePermission(bp);
+ origState.updatePermissionFlags(bp,
PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// If we revoked a permission permission, we have to write.
updatedUserIds = ArrayUtils.appendInt(
updatedUserIds, userId);
}
- }
- // Grant an install permission.
- if (permissionsState.grantInstallPermission(bp) !=
- PERMISSION_OPERATION_FAILURE) {
- changedInstallPermission = true;
- }
- } break;
+ // Grant an install permission.
+ if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
+ changedInstallPermission = true;
+ }
+ } break;
- case GRANT_RUNTIME: {
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
+ case GRANT_RUNTIME: {
+ boolean hardRestricted = bp.isHardRestricted();
+ boolean softRestricted = bp.isSoftRestricted();
- for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
@@ -2834,25 +2821,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
- PermissionState permState = origPermissions
- .getRuntimePermissionState(perm, userId);
- int flags = permState != null ? permState.getFlags() : 0;
+ PermissionState origPermState = origState.getPermissionState(perm);
+ int flags = origPermState != null ? origPermState.getFlags() : 0;
boolean wasChanged = false;
boolean restrictionExempt =
- (origPermissions.getPermissionFlags(bp.name, userId)
+ (origState.getPermissionFlags(bp.name)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origPermissions.getPermissionFlags(
- bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean restrictionApplied = (origState.getPermissionFlags(
+ bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
- if (permState != null && permState.isGranted()
- && permissionsState.revokeRuntimePermission(
- bp, userId) != PERMISSION_OPERATION_FAILURE) {
+ if (origPermState != null && origPermState.isGranted()
+ && uidState.revokePermission(
+ bp) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
@@ -2882,15 +2868,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
- if (permState != null && permState.isGranted()) {
- if (permissionsState.grantRuntimePermission(bp, userId)
+ if (origPermState != null && origPermState.isGranted()) {
+ if (uidState.grantPermission(bp)
== PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
}
} else {
- if (permState == null) {
+ if (origPermState == null) {
// New permission
if (PLATFORM_PACKAGE_NAME.equals(
bp.getSourcePackageName())) {
@@ -2902,8 +2888,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- if (!permissionsState.hasRuntimePermission(bp.name, userId)
- && permissionsState.grantRuntimePermission(bp, userId)
+ if (!uidState.hasPermission(bp.name)
+ && uidState.grantPermission(bp)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
@@ -2936,36 +2922,32 @@ public class PermissionManagerService extends IPermissionManager.Stub {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } break;
-
- case GRANT_UPGRADE: {
- // Upgrade from Pre-Q to Q permission model. Make all permissions
- // runtime
- PermissionState permState = origPermissions
- .getInstallPermissionState(perm);
- int flags = (permState != null) ? permState.getFlags() : 0;
-
- BasePermission bpToRevoke =
- upgradedActivityRecognitionPermission == null
- ? bp : mSettings.getPermissionLocked(
- upgradedActivityRecognitionPermission);
- // Remove install permission
- if (origPermissions.revokeInstallPermission(bpToRevoke)
- != PERMISSION_OPERATION_FAILURE) {
- origPermissions.updatePermissionFlags(bpToRevoke,
- UserHandle.USER_ALL,
- (MASK_PERMISSION_FLAGS_ALL
- & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
- changedInstallPermission = true;
- }
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+ flags);
+ } break;
+
+ case GRANT_UPGRADE: {
+ // Upgrade from Pre-Q to Q permission model. Make all permissions
+ // runtime
+ PermissionState origPermState = origState.getPermissionState(perm);
+ int flags = (origPermState != null) ? origPermState.getFlags() : 0;
+
+ BasePermission bpToRevoke =
+ upgradedActivityRecognitionPermission == null
+ ? bp : mSettings.getPermissionLocked(
+ upgradedActivityRecognitionPermission);
+ // Remove install permission
+ if (origState.revokePermission(bpToRevoke)
+ != PERMISSION_OPERATION_FAILURE) {
+ origState.updatePermissionFlags(bpToRevoke,
+ (MASK_PERMISSION_FLAGS_ALL
+ & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
+ changedInstallPermission = true;
+ }
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
+ boolean hardRestricted = bp.isHardRestricted();
+ boolean softRestricted = bp.isSoftRestricted();
- for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
@@ -2976,18 +2958,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
boolean wasChanged = false;
boolean restrictionExempt =
- (origPermissions.getPermissionFlags(bp.name, userId)
+ (origState.getPermissionFlags(bp.name)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origPermissions.getPermissionFlags(
- bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean restrictionApplied = (origState.getPermissionFlags(
+ bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
- if (permState != null && permState.isGranted()
- && permissionsState.revokeRuntimePermission(
- bp, userId) != PERMISSION_OPERATION_FAILURE) {
+ if (origPermState != null && origPermState.isGranted()
+ && uidState.revokePermission(
+ bp) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
@@ -3017,15 +2999,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized ||
(!hardRestricted || restrictionExempt)) {
- if (permissionsState.grantRuntimePermission(bp, userId) !=
- PERMISSION_OPERATION_FAILURE) {
+ if (uidState.grantPermission(bp)
+ != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
} else {
- if (!permissionsState.hasRuntimePermission(bp.name, userId)
- && permissionsState.grantRuntimePermission(bp,
- userId) != PERMISSION_OPERATION_FAILURE) {
+ if (!uidState.hasPermission(bp.name)
+ && uidState.grantPermission(bp)
+ != PERMISSION_OPERATION_FAILURE) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
@@ -3058,72 +3040,75 @@ public class PermissionManagerService extends IPermissionManager.Stub {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- permissionsState.updatePermissionFlags(bp, userId,
+ uidState.updatePermissionFlags(bp,
MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } break;
+ } break;
- default: {
- if (packageOfInterest == null
- || packageOfInterest.equals(pkg.getPackageName())) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.getPackageName()
- + " because it was previously installed without");
+ default: {
+ if (packageOfInterest == null
+ || packageOfInterest.equals(pkg.getPackageName())) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + pkg.getPackageName()
+ + " because it was previously installed without");
+ }
}
- }
- } break;
- }
- } else {
- if (permissionsState.revokeInstallPermission(bp) !=
- PERMISSION_OPERATION_FAILURE) {
- // Also drop the permission flags.
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
- changedInstallPermission = true;
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Un-granting permission " + perm
- + " from package " + pkg.getPackageName()
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
- + ")");
+ } break;
}
- } else if (bp.isAppOp()) {
- // Don't print warning for app op permissions, since it is fine for them
- // not to be granted, there is a UI for the user to decide.
- if (DEBUG_PERMISSIONS
- && (packageOfInterest == null
- || packageOfInterest.equals(pkg.getPackageName()))) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.getPackageName()
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
- + ")");
+ } else {
+ if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) {
+ // Also drop the permission flags.
+ uidState.updatePermissionFlags(bp,
+ MASK_PERMISSION_FLAGS_ALL, 0);
+ changedInstallPermission = true;
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Un-granting permission " + perm
+ + " from package " + pkg.getPackageName()
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
+ }
+ } else if (bp.isAppOp()) {
+ // Don't print warning for app op permissions, since it is fine for them
+ // not to be granted, there is a UI for the user to decide.
+ if (DEBUG_PERMISSIONS
+ && (packageOfInterest == null
+ || packageOfInterest.equals(pkg.getPackageName()))) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + pkg.getPackageName()
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
+ }
}
}
}
}
- }
- if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
- !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
- // This is the first that we have heard about this package, so the
- // permissions we have now selected are fixed until explicitly
- // changed.
- ps.setInstallPermissionsFixed(true);
- }
+ if ((changedInstallPermission || replace)
+ && !userState.areInstallPermissionsFixed(ps.name)
+ && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
+ // This is the first that we have heard about this package, so the
+ // permissions we have now selected are fixed until explicitly
+ // changed.
+ userState.setInstallPermissionsFixed(ps.name, true);
+ }
- synchronized (mLock) {
- updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
- userIds, updatedUserIds);
- updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
- permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds);
- updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
- updatedUserIds);
+ synchronized (mLock) {
+ updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg,
+ userId, updatedUserIds);
+ updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState,
+ uidState, pkg, newImplicitPermissions, userId, updatedUserIds);
+ }
}
+ updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
+ updatedUserIds);
+
// TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important
// for shared users.
// Persist the runtime permissions state for users with changes. If permissions
@@ -3159,40 +3144,38 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @return The updated value of the {@code updatedUserIds} parameter
*/
- private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull PermissionsState ps,
- @NonNull AndroidPackage pkg, @NonNull int[] userIds, @NonNull int[] updatedUserIds) {
+ private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps,
+ @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) {
String pkgName = pkg.getPackageName();
boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
>= Build.VERSION_CODES.M;
- for (int userId : userIds) {
- for (String permission : ps.getPermissions(userId)) {
- if (!pkg.getImplicitPermissions().contains(permission)) {
- if (!ps.hasInstallPermission(permission)) {
- int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
+ for (String permission : ps.getPermissions()) {
+ if (!pkg.getImplicitPermissions().contains(permission)) {
+ if (!ps.hasInstallPermission(permission)) {
+ int flags = ps.getPermissionFlags(permission);
- if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
- BasePermission bp = mSettings.getPermissionLocked(permission);
+ if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+ BasePermission bp = mSettings.getPermissionLocked(permission);
- int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
- if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
- && supportsRuntimePermissions) {
- int revokeResult = ps.revokeRuntimePermission(bp, userId);
- if (revokeResult != PERMISSION_OPERATION_FAILURE) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Revoking runtime permission "
- + permission + " for " + pkgName
- + " as it is now requested");
- }
+ if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
+ && supportsRuntimePermissions) {
+ int revokeResult = ps.revokePermission(bp);
+ if (revokeResult != PERMISSION_OPERATION_FAILURE) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Revoking runtime permission "
+ + permission + " for " + pkgName
+ + " as it is now requested");
}
-
- flagsToRemove |= USER_PERMISSION_FLAGS;
}
- ps.updatePermissionFlags(bp, userId, flagsToRemove, 0);
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ flagsToRemove |= USER_PERMISSION_FLAGS;
}
+
+ ps.updatePermissionFlags(bp, flagsToRemove, 0);
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
}
}
@@ -3213,12 +3196,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param newPerm The permission to inherit to
* @param ps The permission state of the package
* @param pkg The package requesting the permissions
- * @param userId The user the permission belongs to
*/
private void inheritPermissionStateToNewImplicitPermissionLocked(
@NonNull ArraySet<String> sourcePerms, @NonNull String newPerm,
- @NonNull PermissionsState ps, @NonNull AndroidPackage pkg,
- @UserIdInt int userId) {
+ @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) {
String pkgName = pkg.getPackageName();
boolean isGranted = false;
int flags = 0;
@@ -3226,17 +3207,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
int numSourcePerm = sourcePerms.size();
for (int i = 0; i < numSourcePerm; i++) {
String sourcePerm = sourcePerms.valueAt(i);
- if ((ps.hasRuntimePermission(sourcePerm, userId))
- || ps.hasInstallPermission(sourcePerm)) {
+ if (ps.hasPermission(sourcePerm)) {
if (!isGranted) {
flags = 0;
}
isGranted = true;
- flags |= ps.getPermissionFlags(sourcePerm, userId);
+ flags |= ps.getPermissionFlags(sourcePerm);
} else {
if (!isGranted) {
- flags |= ps.getPermissionFlags(sourcePerm, userId);
+ flags |= ps.getPermissionFlags(sourcePerm);
}
}
}
@@ -3247,11 +3227,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
+ " for " + pkgName);
}
- ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
+ ps.grantPermission(mSettings.getPermissionLocked(newPerm));
}
// Add permission flags
- ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags);
+ ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags);
}
/**
@@ -3283,15 +3263,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param origPs The permission state of the package before the split
* @param ps The new permission state
* @param pkg The package the permission belongs to
- * @param userIds All user IDs in the system, must be passed in because this method is locked
+ * @param userId The user ID
* @param updatedUserIds List of users for which the permission state has already been changed
*
* @return List of users for which the permission state has been changed
*/
private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked(
- @NonNull PermissionsState origPs, @NonNull PermissionsState ps,
+ @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps,
@NonNull AndroidPackage pkg, @NonNull ArraySet<String> newImplicitPermissions,
- @NonNull int[] userIds, @NonNull int[] updatedUserIds) {
+ @UserIdInt int userId, @NonNull int[] updatedUserIds) {
String pkgName = pkg.getPackageName();
ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
@@ -3325,35 +3305,33 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (!ps.hasInstallPermission(newPerm)) {
BasePermission bp = mSettings.getPermissionLocked(newPerm);
- for (int userId : userIds) {
- if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
- ps.updatePermissionFlags(bp, userId,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
- }
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
+ ps.updatePermissionFlags(bp,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ }
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- boolean inheritsFromInstallPerm = false;
- for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
- sourcePermNum++) {
- if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
- inheritsFromInstallPerm = true;
- break;
- }
+ boolean inheritsFromInstallPerm = false;
+ for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
+ sourcePermNum++) {
+ if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
+ inheritsFromInstallPerm = true;
+ break;
}
+ }
- if (!origPs.hasRequestedPermission(sourcePerms)
- && !inheritsFromInstallPerm) {
- // Both permissions are new so nothing to inherit.
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
- + " for " + pkgName + " as split permission is also new");
- }
- } else {
- // Inherit from new install or existing runtime permissions
- inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
- newPerm, ps, pkg, userId);
+ if (!origPs.hasRequestedPermission(sourcePerms)
+ && !inheritsFromInstallPerm) {
+ // Both permissions are new so nothing to inherit.
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+ + " for " + pkgName + " as split permission is also new");
}
+ } else {
+ // Inherit from new install or existing runtime permissions
+ inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
+ newPerm, ps, pkg);
}
}
}
@@ -3484,7 +3462,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean shouldGrantSignaturePermission(String perm, AndroidPackage pkg,
- PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) {
+ PackageSetting pkgSetting, BasePermission bp, UidPermissionState origPermissions) {
boolean oemPermission = bp.isOEM();
boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
@@ -3760,12 +3738,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Legacy apps have the permission and get user consent on launch.
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return false;
}
- return permissionsState.isPermissionReviewRequired(userId);
+ return uidState.isPermissionReviewRequired();
}
private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) {
@@ -3789,9 +3768,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
@@ -3816,7 +3796,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
&& (supportsRuntimePermissions || !bp.isRuntimeOnly())
&& (grantedPermissions == null
|| ArrayUtils.contains(grantedPermissions, permission))) {
- final int flags = permissionsState.getPermissionFlags(permission, userId);
+ final int flags = uidState.getPermissionFlags(permission);
if (supportsRuntimePermissions) {
// Installer cannot change immutable permissions.
if ((flags & immutableFlags) == 0) {
@@ -3838,18 +3818,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg,
@UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid,
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
- return;
- }
-
SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>();
boolean updatePermissions = false;
final int permissionCount = pkg.getRequestedPermissions().size();
for (int i = 0; i < userIds.length; i++) {
int userId = userIds[i];
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
+ continue;
+ }
+
for (int j = 0; j < permissionCount; j++) {
final String permissionName = pkg.getRequestedPermissions().get(j);
@@ -3859,14 +3840,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
continue;
}
- if (permissionsState.hasPermission(permissionName, userId)) {
+ if (uidState.hasPermission(permissionName)) {
if (oldGrantedRestrictedPermissions.get(userId) == null) {
oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
}
oldGrantedRestrictedPermissions.get(userId).add(permissionName);
}
- final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
+ final int oldFlags = uidState.getPermissionFlags(permissionName);
int newFlags = oldFlags;
int mask = 0;
@@ -3921,8 +3902,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = permissionsState.hasPermission(permissionName,
- userId);
+ final boolean isGranted = uidState.hasPermission(permissionName);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3958,12 +3938,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int j = 0; j < oldGrantedCount; j++) {
final String permission = oldPermsForUser.valueAt(j);
// Sometimes we create a new permission state instance during update.
- final PermissionsState newPermissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState newUidState = getUidState(pkg, userId);
+ if (newUidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+ + " and user " + userId);
continue;
}
- if (!newPermissionsState.hasPermission(permission, userId)) {
+ if (!newUidState.hasPermission(permission)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
@@ -4012,9 +3993,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
continue;
}
- PermissionsState permissionsState = getPermissionsState(deletedPs.pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName());
+ UidPermissionState uidState = getUidState(deletedPs.pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
+ + " and user " + userId);
continue;
}
@@ -4036,25 +4018,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- // Try to revoke as an install permission which is for all users.
// The package is gone - no need to keep flags for applying policy.
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
-
- if (permissionsState.revokeInstallPermission(bp)
- == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- affectedUserId = UserHandle.USER_ALL;
- }
+ uidState.updatePermissionFlags(bp, PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// Try to revoke as a runtime permission which is per user.
- if (permissionsState.revokeRuntimePermission(bp, userId)
+ // TODO(zhanghai): This doesn't make sense. revokePermission() doesn't fail, and why are
+ // we only killing the uid when gids changed, instead of any permission change?
+ if (uidState.revokePermission(bp)
== PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- if (affectedUserId == UserHandle.USER_NULL) {
- affectedUserId = userId;
- } else if (affectedUserId != userId) {
- // Multiple users affected.
- affectedUserId = UserHandle.USER_ALL;
- }
+ affectedUserId = userId;
}
}
@@ -4062,12 +4034,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@GuardedBy("mLock")
- private int[] revokeUnusedSharedUserPermissionsLocked(
- List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) {
+ private boolean revokeUnusedSharedUserPermissionsLocked(
+ List<AndroidPackage> pkgList, UidPermissionState uidState) {
// Collect all used permissions in the UID
final ArraySet<String> usedPermissions = new ArraySet<>();
if (pkgList == null || pkgList.size() == 0) {
- return EmptyArray.INT;
+ return false;
}
for (AndroidPackage pkg : pkgList) {
if (pkg.getRequestedPermissions().isEmpty()) {
@@ -4083,44 +4055,27 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- // Prune install permissions
- List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
- final int installPermCount = installPermStates.size();
- for (int i = installPermCount - 1; i >= 0; i--) {
- PermissionState permissionState = installPermStates.get(i);
+ boolean runtimePermissionChanged = false;
+
+ // Prune permissions
+ final List<com.android.server.pm.permission.PermissionState> permissionStates =
+ uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = permissionStatesSize - 1; i >= 0; i--) {
+ PermissionState permissionState = permissionStates.get(i);
if (!usedPermissions.contains(permissionState.getName())) {
BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
if (bp != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
-
- int[] runtimePermissionChangedUserIds = EmptyArray.INT;
-
- // Prune runtime permissions
- for (int userId : allUserIds) {
- List<PermissionState> runtimePermStates = permissionsState
- .getRuntimePermissionStates(userId);
- final int runtimePermCount = runtimePermStates.size();
- for (int i = runtimePermCount - 1; i >= 0; i--) {
- PermissionState permissionState = runtimePermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, 0);
- runtimePermissionChangedUserIds = ArrayUtils.appendInt(
- runtimePermissionChangedUserIds, userId);
+ uidState.revokePermission(bp);
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, 0);
+ if (permissionState.isRuntime()) {
+ runtimePermissionChanged = true;
}
}
}
}
- return runtimePermissionChangedUserIds;
+ return runtimePermissionChanged;
}
/**
@@ -4368,15 +4323,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
} else {
mPackageManagerInt.forEachPackage(p -> {
- final PermissionsState permissionsState = getPermissionsState(p);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + p.getPackageName());
- return;
- }
- if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
+ final int[] userIds = mUserManagerInt.getUserIds();
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidState(p, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for "
+ + p.getPackageName() + " and user " + userId);
+ return;
+ }
+ if (uidState.getPermissionState(bp.getName()) != null) {
+ uidState.revokePermission(bp);
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+ 0);
+ }
}
});
}
@@ -4784,62 +4743,124 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Nullable
- private PermissionsState getPermissionsState(@NonNull PackageSetting ps) {
- return getPermissionsState(ps.getAppId());
- }
-
- @Nullable
- private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) {
- return getPermissionsState(pkg.getUid());
- }
-
- @Nullable
- private PermissionsState getPermissionsState(int appId) {
- synchronized (mLock) {
- return mAppIdStates.get(appId);
- }
+ private UidPermissionState getUidState(@NonNull PackageSetting ps,
+ @UserIdInt int userId) {
+ return getUidState(ps.getAppId(), userId);
}
@Nullable
- private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) {
- return getOrCreatePermissionsState(ps.getAppId());
+ private UidPermissionState getUidState(@NonNull AndroidPackage pkg,
+ @UserIdInt int userId) {
+ return getUidState(pkg.getUid(), userId);
}
@Nullable
- private PermissionsState getOrCreatePermissionsState(int appId) {
+ private UidPermissionState getUidState(int appId, @UserIdInt int userId) {
synchronized (mLock) {
- PermissionsState state = mAppIdStates.get(appId);
- if (state == null) {
- state = new PermissionsState();
- mAppIdStates.put(appId, state);
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ return null;
}
- return state;
+ return userState.getUidState(appId);
}
}
- private void removePermissionsState(int appId) {
+ private void removeAppState(int appId) {
synchronized (mLock) {
- mAppIdStates.remove(appId);
+ final int[] userIds = mState.getUserIds();
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getUserState(userId);
+ userState.removeUidState(appId);
+ }
}
}
- private void readPermissionsStateFromPackageSettings() {
+ private void readStateFromPackageSettings() {
+ final int[] userIds = getAllUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
+ final int appId = ps.getAppId();
+ final PermissionsState permissionsState = ps.getPermissionsState();
+
synchronized (mLock) {
- mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState()));
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+
+ userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
+ final UidPermissionState uidState = userState.getOrCreateUidState(appId);
+ uidState.reset();
+ uidState.setGlobalGids(permissionsState.getGlobalGids());
+ uidState.setMissing(permissionsState.isMissing(userId));
+ readStateFromPermissionStates(uidState,
+ permissionsState.getInstallPermissionStates(), false);
+ readStateFromPermissionStates(uidState,
+ permissionsState.getRuntimePermissionStates(userId), true);
+ }
}
});
}
- private void writePermissionsStateToPackageSettings() {
+ private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
+ @NonNull List<PermissionsState.PermissionState> permissionStates, boolean isRuntime) {
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionsState.PermissionState permissionState = permissionStates.get(i);
+ final BasePermission permission = permissionState.getPermission();
+ uidState.putPermissionState(permission, isRuntime, permissionState.isGranted(),
+ permissionState.getFlags());
+ }
+ }
+
+ private void writeStateToPackageSettings() {
+ final int[] userIds = mState.getUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
+ ps.setInstallPermissionsFixed(false);
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ permissionsState.reset();
+ final int appId = ps.getAppId();
+
synchronized (mLock) {
- final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId());
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + ps.name);
- return;
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ Slog.e(TAG, "Missing user state for " + userId);
+ continue;
+ }
+
+ if (userState.areInstallPermissionsFixed(ps.name)) {
+ ps.setInstallPermissionsFixed(true);
+ }
+
+ final UidPermissionState uidState = userState.getUidState(appId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permission state for " + ps.name + " and user "
+ + userId);
+ continue;
+ }
+
+ permissionsState.setGlobalGids(uidState.getGlobalGids());
+ permissionsState.setMissing(uidState.isMissing(), userId);
+ final List<PermissionState> permissionStates = uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionState permissionState = permissionStates.get(i);
+
+ final BasePermission permission = permissionState.getPermission();
+ if (permissionState.isGranted()) {
+ if (permissionState.isRuntime()) {
+ permissionsState.grantRuntimePermission(permission, userId);
+ } else {
+ permissionsState.grantInstallPermission(permission);
+ }
+ }
+ final int flags = permissionState.getFlags();
+ if (flags != 0) {
+ final int flagsUserId = permissionState.isRuntime() ? userId
+ : UserHandle.USER_ALL;
+ permissionsState.updatePermissionFlags(permission, flagsUserId, flags,
+ flags);
+ }
+ }
}
- ps.getPermissionsState().copyFrom(permissionsState);
}
});
}
@@ -4876,12 +4897,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
}
@Override
- public void readPermissionsStateFromPackageSettingsTEMP() {
- PermissionManagerService.this.readPermissionsStateFromPackageSettings();
+ public void readStateFromPackageSettingsTEMP() {
+ PermissionManagerService.this.readStateFromPackageSettings();
}
@Override
- public void writePermissionsStateToPackageSettingsTEMP() {
- PermissionManagerService.this.writePermissionsStateToPackageSettings();
+ public void writeStateToPackageSettingsTEMP() {
+ PermissionManagerService.this.writeStateToPackageSettings();
}
@Override
public void onUserRemoved(@UserIdInt int userId) {
@@ -4889,7 +4910,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
public void removePermissionsStateTEMP(int appId) {
- PermissionManagerService.this.removePermissionsState(appId);
+ PermissionManagerService.this.removeAppState(appId);
}
@Override
@UserIdInt
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index f319bf495e8b..7f6a1d4284d2 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -266,21 +266,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
/**
- * Read {@code PermissionsState} from package settings.
+ * Read permission state from package settings.
*
* TODO(zhanghai): This is a temporary method because we should not expose
* {@code PackageSetting} which is a implementation detail that permission should not know.
* Instead, it should retrieve the legacy state via a defined API.
*/
- public abstract void readPermissionsStateFromPackageSettingsTEMP();
+ public abstract void readStateFromPackageSettingsTEMP();
/**
- * Write {@code PermissionsState} from to settings.
+ * Write permission state to package settings.
*
* TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
* for permission.
*/
- public abstract void writePermissionsStateToPackageSettingsTEMP();
+ public abstract void writeStateToPackageSettingsTEMP();
/**
* Notify that a user has been removed and its permission state should be removed as well.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java
new file mode 100644
index 000000000000..2ed9a50353d4
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionState.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2020 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.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * State for a single permission.
+ */
+public final class PermissionState {
+
+ @NonNull
+ private final BasePermission mPermission;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mRuntime;
+
+ @GuardedBy("mLock")
+ private boolean mGranted;
+
+ @GuardedBy("mLock")
+ private int mFlags;
+
+ public PermissionState(@NonNull BasePermission permission, boolean isRuntime) {
+ mPermission = permission;
+ mRuntime = isRuntime;
+ }
+
+ public PermissionState(@NonNull PermissionState other) {
+ this(other.mPermission, other.mRuntime);
+
+ mGranted = other.mGranted;
+ mFlags = other.mFlags;
+ }
+
+ @NonNull
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
+ @NonNull
+ public String getName() {
+ return mPermission.getName();
+ }
+
+ @Nullable
+ public int[] computeGids(@UserIdInt int userId) {
+ return mPermission.computeGids(userId);
+ }
+
+ public boolean isRuntime() {
+ synchronized (mLock) {
+ return mRuntime;
+ }
+ }
+
+ public boolean isGranted() {
+ synchronized (mLock) {
+ return mGranted;
+ }
+ }
+
+ public boolean grant() {
+ synchronized (mLock) {
+ if (mGranted) {
+ return false;
+ }
+ mGranted = true;
+ UidPermissionState.invalidateCache();
+ return true;
+ }
+ }
+
+ public boolean revoke() {
+ synchronized (mLock) {
+ if (!mGranted) {
+ return false;
+ }
+ mGranted = false;
+ UidPermissionState.invalidateCache();
+ return true;
+ }
+ }
+
+ public int getFlags() {
+ synchronized (mLock) {
+ return mFlags;
+ }
+ }
+
+ public boolean updateFlags(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ final int newFlags = flagValues & flagMask;
+
+ // Okay to do before the modification because we hold the lock.
+ UidPermissionState.invalidateCache();
+
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~flagMask) | newFlags;
+ return mFlags != oldFlags;
+ }
+ }
+
+ public boolean isDefault() {
+ synchronized (mLock) {
+ return !mGranted && mFlags == 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index bad59cb1b567..4fb2d5fc200e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -86,6 +86,10 @@ public final class PermissionsState {
copyFrom(prototype);
}
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
/**
* Sets the global gids, applicable to all users.
*
@@ -825,7 +829,7 @@ public final class PermissionsState {
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new PermissionState(mPerm.getName());
+ userState = new PermissionState(mPerm);
mUserStates.put(userId, userState);
}
@@ -908,7 +912,7 @@ public final class PermissionsState {
}
return userState.mFlags != oldFlags;
} else if (newFlags != 0) {
- userState = new PermissionState(mPerm.getName());
+ userState = new PermissionState(mPerm);
userState.mFlags = newFlags;
mUserStates.put(userId, userState);
return true;
@@ -929,16 +933,16 @@ public final class PermissionsState {
}
public static final class PermissionState {
- private final String mName;
+ private final BasePermission mPermission;
private boolean mGranted;
private int mFlags;
- public PermissionState(String name) {
- mName = name;
+ public PermissionState(BasePermission permission) {
+ mPermission = permission;
}
public PermissionState(PermissionState other) {
- mName = other.mName;
+ mPermission = other.mPermission;
mGranted = other.mGranted;
mFlags = other.mFlags;
}
@@ -947,8 +951,12 @@ public final class PermissionsState {
return !mGranted && mFlags == 0;
}
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
public String getName() {
- return mName;
+ return mPermission.getName();
}
public boolean isGranted() {
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
new file mode 100644
index 000000000000..4c047ffd30e8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2015 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.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Permission state for a UID.
+ * <p>
+ * This class is also responsible for keeping track of the Linux GIDs per
+ * user for a package or a shared user. The GIDs are computed as a set of
+ * the GIDs for all granted permissions' GIDs on a per user basis.
+ */
+public final class UidPermissionState {
+ /** The permission operation failed. */
+ public static final int PERMISSION_OPERATION_FAILURE = -1;
+
+ /** The permission operation succeeded and no gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS = 0;
+
+ /** The permission operation succeeded and gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
+
+ private static final int[] NO_GIDS = {};
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private ArrayMap<String, PermissionState> mPermissions;
+
+ @NonNull
+ private int[] mGlobalGids = NO_GIDS;
+
+ private boolean mMissing;
+
+ private boolean mPermissionReviewRequired;
+
+ public UidPermissionState() {
+ /* do nothing */
+ }
+
+ public UidPermissionState(@NonNull UidPermissionState prototype) {
+ copyFrom(prototype);
+ }
+
+ /**
+ * Gets the global gids, applicable to all users.
+ */
+ @NonNull
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(@NonNull int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ }
+ }
+
+ static void invalidateCache() {
+ PackageManager.invalidatePackageInfoCache();
+ }
+
+ /**
+ * Initialized this instance from another one.
+ *
+ * @param other The other instance.
+ */
+ public void copyFrom(@NonNull UidPermissionState other) {
+ if (other == this) {
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ if (other.mPermissions == null) {
+ mPermissions = null;
+ } else {
+ mPermissions.clear();
+ }
+ }
+ if (other.mPermissions != null) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ final int permissionCount = other.mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String name = other.mPermissions.keyAt(i);
+ PermissionState permissionState = other.mPermissions.valueAt(i);
+ mPermissions.put(name, new PermissionState(permissionState));
+ }
+ }
+ }
+
+ mGlobalGids = NO_GIDS;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = other.mGlobalGids.clone();
+ }
+
+ mMissing = other.mMissing;
+
+ mPermissionReviewRequired = other.mPermissionReviewRequired;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final UidPermissionState other = (UidPermissionState) obj;
+
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ if (other.mPermissions != null) {
+ return false;
+ }
+ } else if (!mPermissions.equals(other.mPermissions)) {
+ return false;
+ }
+ }
+
+ if (mMissing != other.mMissing) {
+ return false;
+ }
+
+ if (mPermissionReviewRequired != other.mPermissionReviewRequired) {
+ return false;
+ }
+ return Arrays.equals(mGlobalGids, other.mGlobalGids);
+ }
+
+ /**
+ * Check whether the permissions state is missing for a user. This can happen if permission
+ * state is rolled back and we'll need to generate a reasonable default state to keep the app
+ * usable.
+ */
+ public boolean isMissing() {
+ return mMissing;
+ }
+
+ /**
+ * Set whether the permissions state is missing for a user. This can happen if permission state
+ * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
+ */
+ public void setMissing(boolean missing) {
+ mMissing = missing;
+ }
+
+ public boolean isPermissionReviewRequired() {
+ return mPermissionReviewRequired;
+ }
+
+ /**
+ * Gets whether the state has a given permission.
+ *
+ * @param name The permission name.
+ * @return Whether the state has the permission.
+ */
+ public boolean hasPermission(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted();
+ }
+ }
+
+ /**
+ * Gets whether the state has a given install permission.
+ *
+ * @param name The permission name.
+ * @return Whether the state has the install permission.
+ */
+ public boolean hasInstallPermission(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted()
+ && !permissionState.isRuntime();
+ }
+ }
+
+ /**
+ * Returns whether the state has any known request for the given permission name,
+ * whether or not it has been granted.
+ *
+ * @deprecated Not all requested permissions may be here.
+ */
+ @Deprecated
+ public boolean hasRequestedPermission(@NonNull ArraySet<String> names) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ for (int i = names.size() - 1; i >= 0; i--) {
+ if (mPermissions.get(names.valueAt(i)) != null) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the state has any known request for the given permission name,
+ * whether or not it has been granted.
+ *
+ * @deprecated Not all requested permissions may be here.
+ */
+ @Deprecated
+ public boolean hasRequestedPermission(@NonNull String name) {
+ return mPermissions != null && (mPermissions.get(name) != null);
+ }
+
+ /**
+ * Gets all permissions for a given device user id regardless if they
+ * are install time or runtime permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ @NonNull
+ public Set<String> getPermissions() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
+
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+
+ if (hasPermission(permission)) {
+ permissions.add(permission);
+ }
+ }
+
+ return permissions;
+ }
+ }
+
+ /**
+ * Gets the flags for a permission.
+ *
+ * @param name The permission name.
+ * @return The permission state or null if no such.
+ */
+ public int getPermissionFlags(@NonNull String name) {
+ PermissionState permState = getPermissionState(name);
+ if (permState != null) {
+ return permState.getFlags();
+ }
+ return 0;
+ }
+
+ /**
+ * Update the flags associated with a given permission.
+ * @param permission The permission whose flags to update.
+ * @param flagMask Mask for which flags to change.
+ * @param flagValues New values for the mask flags.
+ * @return Whether the permission flags changed.
+ */
+ public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
+ int flagValues) {
+ if (flagMask == 0) {
+ return false;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ final int oldFlags = permissionState.getFlags();
+
+ synchronized (mLock) {
+ final boolean updated = permissionState.updateFlags(flagMask, flagValues);
+ if (updated) {
+ final int newFlags = permissionState.getFlags();
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
+ mPermissionReviewRequired = false;
+ }
+ }
+ }
+ return updated;
+ }
+ }
+
+ private boolean hasPermissionRequiringReview() {
+ synchronized (mLock) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ final PermissionState permission = mPermissions.valueAt(i);
+ if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ boolean changed = false;
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ changed |= permissionState.updateFlags(flagMask, flagValues);
+ }
+ return changed;
+ }
+ }
+
+ /**
+ * Compute the Linux gids for a given device user from the permissions
+ * granted to this user. Note that these are computed to avoid additional
+ * state as they are rarely accessed.
+ *
+ * @param userId The device user id.
+ * @return The gids for the device user.
+ */
+ @NonNull
+ public int[] computeGids(@UserIdInt int userId) {
+ int[] gids = mGlobalGids;
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ if (!permissionState.isGranted()) {
+ continue;
+ }
+ final int[] permGids = permissionState.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux gids for all device users from the permissions
+ * granted to these users.
+ *
+ * @return The gids for all device users.
+ */
+ @NonNull
+ public int[] computeGids(@NonNull int[] userIds) {
+ int[] gids = mGlobalGids;
+
+ for (int userId : userIds) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ /**
+ * Resets the internal state of this object.
+ */
+ public void reset() {
+ mGlobalGids = NO_GIDS;
+
+ synchronized (mLock) {
+ mPermissions = null;
+ invalidateCache();
+ }
+
+ mMissing = false;
+ mPermissionReviewRequired = false;
+ }
+
+ /**
+ * Gets the state for a permission or null if no such.
+ *
+ * @param name The permission name.
+ * @return The permission state.
+ */
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return null;
+ }
+ return mPermissions.get(name);
+ }
+ }
+
+ /**
+ * Gets all permission states.
+ *
+ * @return The permission states or an empty set.
+ */
+ @NonNull
+ public List<PermissionState> getPermissionStates() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(mPermissions.values());
+ }
+ }
+
+ /**
+ * Put a permission state.
+ */
+ public void putPermissionState(@NonNull BasePermission permission, boolean isRuntime,
+ boolean isGranted, int flags) {
+ synchronized (mLock) {
+ ensureNoPermissionState(permission.name);
+ PermissionState permissionState = ensurePermissionState(permission, isRuntime);
+ if (isGranted) {
+ permissionState.grant();
+ }
+ permissionState.updateFlags(flags, flags);
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ }
+ }
+ }
+
+ /**
+ * Grant a permission.
+ *
+ * @param permission The permission to grant.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int grantPermission(@NonNull BasePermission permission) {
+ if (hasPermission(permission.getName())) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ if (!permissionState.grant()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
+ }
+
+ /**
+ * Revoke a permission.
+ *
+ * @param permission The permission to revoke.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int revokePermission(@NonNull BasePermission permission) {
+ final String permissionName = permission.getName();
+ if (!hasPermission(permissionName)) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState;
+ synchronized (mLock) {
+ permissionState = mPermissions.get(permissionName);
+ }
+
+ if (!permissionState.revoke()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ if (permissionState.isDefault()) {
+ ensureNoPermissionState(permissionName);
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
+ }
+
+ // TODO: fix this to use arraycopy and append all ints in one go
+ private static int[] appendInts(int[] current, int[] added) {
+ if (current != null && added != null) {
+ for (int guid : added) {
+ current = ArrayUtils.appendInt(current, guid);
+ }
+ }
+ return current;
+ }
+
+ @NonNull
+ private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
+ return ensurePermissionState(permission, permission.isRuntime());
+ }
+
+ @NonNull
+ private PermissionState ensurePermissionState(@NonNull BasePermission permission,
+ boolean isRuntime) {
+ final String permissionName = permission.getName();
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ PermissionState permissionState = mPermissions.get(permissionName);
+ if (permissionState == null) {
+ permissionState = new PermissionState(permission, isRuntime);
+ mPermissions.put(permissionName, permissionState);
+ }
+ return permissionState;
+ }
+ }
+
+ private void ensureNoPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return;
+ }
+ mPermissions.remove(name);
+ if (mPermissions.isEmpty()) {
+ mPermissions = null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
new file mode 100644
index 000000000000..7f55cb161e40
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 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.server.pm.permission;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Permission state for a user.
+ */
+public final class UserPermissionState {
+ /**
+ * Whether the install permissions have been granted to a package, so that no install
+ * permissions should be added to it unless the package is upgraded.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>();
+
+ /**
+ * Maps from app ID to {@link UidPermissionState}.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public UserPermissionState(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ public boolean areInstallPermissionsFixed(@NonNull String packageName) {
+ synchronized (mLock) {
+ return mInstallPermissionsFixed.contains(packageName);
+ }
+ }
+
+ public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) {
+ synchronized (mLock) {
+ if (fixed) {
+ mInstallPermissionsFixed.add(packageName);
+ } else {
+ mInstallPermissionsFixed.remove(packageName);
+ }
+ }
+ }
+
+ @Nullable
+ public UidPermissionState getUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ return mUidStates.get(appId);
+ }
+ }
+
+ @NonNull
+ public UidPermissionState getOrCreateUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ UidPermissionState uidState = mUidStates.get(appId);
+ if (uidState == null) {
+ uidState = new UidPermissionState();
+ mUidStates.put(appId, uidState);
+ }
+ return uidState;
+ }
+ }
+
+ public void removeUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ mUidStates.delete(appId);
+ }
+ }
+
+ private void checkAppId(@AppIdInt int appId) {
+ if (UserHandle.getUserId(appId) != 0) {
+ throw new IllegalArgumentException(appId + " is not an app ID");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 06c2b1687fa4..523b484269c0 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -25,6 +25,7 @@ import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -62,8 +63,15 @@ public class TaskTapPointerEventListener implements PointerEventListener {
public void onPointerEvent(MotionEvent motionEvent) {
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
- final int x = (int) motionEvent.getX();
- final int y = (int) motionEvent.getY();
+ final int x;
+ final int y;
+ if (motionEvent.getSource() == InputDevice.SOURCE_MOUSE) {
+ x = (int) motionEvent.getXCursorPosition();
+ y = (int) motionEvent.getYCursorPosition();
+ } else {
+ x = (int) motionEvent.getX();
+ y = (int) motionEvent.getY();
+ }
synchronized (this) {
if (!mTouchExcludeRegion.contains(x, y)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 33b12138db79..e85bbd958898 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1560,17 +1560,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
@GuardedBy("getLockObject()")
private void migrateToProfileOnOrganizationOwnedDeviceIfCompLocked() {
- logIfVerbose("Checking whether we need to migrate COMP ");
+ if (VERBOSE_LOG) Slog.d(LOG_TAG, "Checking whether we need to migrate COMP ");
final int doUserId = mOwners.getDeviceOwnerUserId();
if (doUserId == UserHandle.USER_NULL) {
- logIfVerbose("No DO found, skipping migration.");
+ if (VERBOSE_LOG) Slog.d(LOG_TAG, "No DO found, skipping migration.");
return;
}
final List<UserInfo> profiles = mUserManager.getProfiles(doUserId);
if (profiles.size() != 2) {
if (profiles.size() == 1) {
- logIfVerbose("Profile not found, skipping migration.");
+ if (VERBOSE_LOG) Slog.d(LOG_TAG, "Profile not found, skipping migration.");
} else {
Slog.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
}
@@ -1748,7 +1748,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
final int doUserId = mOwners.getDeviceOwnerUserId();
if (doUserId == UserHandle.USER_NULL) {
- logIfVerbose("No DO found, skipping application of restriction.");
+ if (VERBOSE_LOG) Slog.d(LOG_TAG, "No DO found, skipping application of restriction.");
return;
}
@@ -8126,7 +8126,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
*/
- private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId,
+ private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner,
+ @UserIdInt int userId,
boolean hasIncompatibleAccountsOrNonAdb) {
if (!isAdb()) {
enforceCanManageProfileAndDeviceOwners();
@@ -8134,34 +8135,38 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int code = checkDeviceOwnerProvisioningPreConditionLocked(
owner, userId, isAdb(), hasIncompatibleAccountsOrNonAdb);
+ if (code != CODE_OK) {
+ throw new IllegalStateException(computeProvisioningErrorString(code, userId));
+ }
+ }
+
+ private static String computeProvisioningErrorString(int code, @UserIdInt int userId) {
switch (code) {
case CODE_OK:
- return;
+ return "OK";
case CODE_HAS_DEVICE_OWNER:
- throw new IllegalStateException(
- "Trying to set the device owner, but device owner is already set.");
+ return "Trying to set the device owner, but device owner is already set.";
case CODE_USER_HAS_PROFILE_OWNER:
- throw new IllegalStateException("Trying to set the device owner, but the user "
- + "already has a profile owner.");
+ return "Trying to set the device owner, but the user already has a profile owner.";
case CODE_USER_NOT_RUNNING:
- throw new IllegalStateException("User not running: " + userId);
+ return "User " + userId + " not running.";
case CODE_NOT_SYSTEM_USER:
- throw new IllegalStateException("User is not system user");
+ return "User " + userId + " is not system user.";
case CODE_USER_SETUP_COMPLETED:
- throw new IllegalStateException(
- "Cannot set the device owner if the device is already set-up");
+ return "Cannot set the device owner if the device is already set-up.";
case CODE_NONSYSTEM_USER_EXISTS:
- throw new IllegalStateException("Not allowed to set the device owner because there "
- + "are already several users on the device");
+ return "Not allowed to set the device owner because there are already several"
+ + " users on the device.";
case CODE_ACCOUNTS_NOT_EMPTY:
- throw new IllegalStateException("Not allowed to set the device owner because there "
- + "are already some accounts on the device");
+ return "Not allowed to set the device owner because there are already some accounts"
+ + " on the device.";
case CODE_HAS_PAIRED:
- throw new IllegalStateException("Not allowed to set the device owner because this "
- + "device has already paired");
+ return "Not allowed to set the device owner because this device has already "
+ + "paired.";
default:
- throw new IllegalStateException("Unexpected @ProvisioningPreCondition " + code);
+ return "Unexpected @ProvisioningPreCondition: " + code;
}
+
}
private void enforceUserUnlocked(int userId) {
@@ -11836,11 +11841,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return checkProvisioningPreConditionSkipPermission(action, packageName);
}
- private int checkProvisioningPreConditionSkipPermission(String action, String packageName) {
+ private int checkProvisioningPreConditionSkipPermission(String action,
+ String packageName) {
if (!mHasFeature) {
+ logMissingFeatureAction("Cannot check provisioning for action " + action);
return CODE_DEVICE_ADMIN_NOT_SUPPORTED;
}
+ final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
+ if (code != CODE_OK) {
+ Slog.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
+ + ") failed: "
+ + computeProvisioningErrorString(code, mInjector.userHandleGetCallingUserId()));
+ }
+ return code;
+ }
+ private int checkProvisioningPreConditionSkipPermissionNoLog(String action,
+ String packageName) {
final int callingUserId = mInjector.userHandleGetCallingUserId();
if (action != null) {
switch (action) {
@@ -11863,7 +11880,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* except for adb command if no accounts or additional users are present on the device.
*/
private int checkDeviceOwnerProvisioningPreConditionLocked(@Nullable ComponentName owner,
- int deviceOwnerUserId, boolean isAdb, boolean hasIncompatibleAccountsOrNonAdb) {
+ @UserIdInt int deviceOwnerUserId, boolean isAdb,
+ boolean hasIncompatibleAccountsOrNonAdb) {
if (mOwners.hasDeviceOwner()) {
return CODE_HAS_DEVICE_OWNER;
}
@@ -11908,7 +11926,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private int checkDeviceOwnerProvisioningPreCondition(int deviceOwnerUserId) {
+ private int checkDeviceOwnerProvisioningPreCondition(@UserIdInt int deviceOwnerUserId) {
synchronized (getLockObject()) {
// hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
@@ -11917,7 +11935,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private int checkManagedProfileProvisioningPreCondition(String packageName, int callingUserId) {
+ private int checkManagedProfileProvisioningPreCondition(String packageName,
+ @UserIdInt int callingUserId) {
if (!hasFeatureManagedUsers()) {
return CODE_MANAGED_USERS_NOT_SUPPORTED;
}
@@ -14531,12 +14550,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void logIfVerbose(String message) {
- if (VERBOSE_LOG) {
- Slog.d(LOG_TAG, message);
- }
- }
-
@Override
public void setCommonCriteriaModeEnabled(ComponentName who, boolean enabled) {
final int userId = mInjector.userHandleGetCallingUserId();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 82726c7ea20c..5d8f662301c1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -173,6 +173,8 @@ public class MockingOomAdjusterTests {
mock(OomAdjProfiler.class));
setFieldValue(ActivityManagerService.class, sService, "mUserController",
mock(UserController.class));
+ setFieldValue(ActivityManagerService.class, sService, "mAppProfiler",
+ mock(AppProfiler.class));
doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3c7206fee9d1..3430dbdce753 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -44,6 +44,7 @@ import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;
@@ -1556,6 +1557,49 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(zenRule.getName(), ruleInConfig.name);
}
+ @Test
+ public void testRulesWithSameUri() {
+ Uri sharedUri = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ new ComponentName("android", "ScheduleConditionProvider"),
+ sharedUri,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+ AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
+ new ComponentName("android", "ScheduleConditionProvider"),
+ sharedUri,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id2 = mZenModeHelperSpy.addAutomaticZenRule(zenRule2, "test");
+
+ Condition condition = new Condition(sharedUri, "", Condition.STATE_TRUE);
+ mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition);
+
+ for (ZenModeConfig.ZenRule rule : mZenModeHelperSpy.mConfig.automaticRules.values()) {
+ if (rule.id.equals(id)) {
+ assertNotNull(rule.condition);
+ assertTrue(rule.condition.state == Condition.STATE_TRUE);
+ }
+ if (rule.id.equals(id2)) {
+ assertNotNull(rule.condition);
+ assertTrue(rule.condition.state == Condition.STATE_TRUE);
+ }
+ }
+
+ condition = new Condition(sharedUri, "", Condition.STATE_FALSE);
+ mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition);
+
+ for (ZenModeConfig.ZenRule rule : mZenModeHelperSpy.mConfig.automaticRules.values()) {
+ if (rule.id.equals(id)) {
+ assertNotNull(rule.condition);
+ assertTrue(rule.condition.state == Condition.STATE_FALSE);
+ }
+ if (rule.id.equals(id2)) {
+ assertNotNull(rule.condition);
+ assertTrue(rule.condition.state == Condition.STATE_FALSE);
+ }
+ }
+ }
+
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 470d4bec4c38..4de1abfee7fc 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2743,32 +2743,27 @@ public class CarrierConfigManager {
/**
* Indicates if the carrier supports auto-upgrading a call to RTT when receiving a call from a
* RTT-supported device.
- * @hide
*/
public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
/**
* Indicates if the carrier supports RTT during a video call.
- * @hide
*/
public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
/**
* Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
- * @hide
*/
public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
/**
* Indicates if the carrier supports downgrading a RTT call to a voice call during the call.
- * @hide
*/
public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
/**
* Indicates if the TTY HCO and VCO options should be hidden in the accessibility menu
* if the device is capable of RTT.
- * @hide
*/
public static final String KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL = "hide_tty_hco_vco_with_rtt";
diff --git a/tests/SilkFX/res/layout/activity_glass.xml b/tests/SilkFX/res/layout/activity_glass.xml
index a7b76bdeba09..aa09f276d5c8 100644
--- a/tests/SilkFX/res/layout/activity_glass.xml
+++ b/tests/SilkFX/res/layout/activity_glass.xml
@@ -41,7 +41,16 @@
app:layout_constraintBottom_toTopOf="@+id/bottomPanel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
+ app:layout_constraintTop_toTopOf="parent">
+ <TextView
+ android:id="@+id/textOverlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18dp"
+ android:layout_gravity="center"
+ android:textColor="#ffffff"
+ android:text="Lorem Ipsum dolor sit amet." />
+ </com.android.test.silkfx.materials.GlassView>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bottomPanel"
@@ -90,7 +99,7 @@
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:max="150"
- android:progress="20"
+ android:progress="40"
app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
@@ -118,7 +127,7 @@
android:layout_marginEnd="12dp"
android:layout_marginBottom="24dp"
android:max="100"
- android:progress="5"
+ android:progress="15"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
@@ -141,7 +150,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginBottom="8dp"
- android:text="Material Opacity"
+ android:text="Soft light Opacity"
android:textColor="@android:color/white"
app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
app:layout_constraintStart_toStartOf="parent" />
@@ -219,6 +228,19 @@
app:layout_constraintStart_toEndOf="@+id/background2"
android:src="@drawable/background3" />
+ <Button
+ android:id="@+id/pickImage"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerCrop"
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:onClick="onPickImageClick"
+ app:layout_constraintBottom_toBottomOf="@+id/background1"
+ app:layout_constraintStart_toEndOf="@+id/background3"
+ android:text="Pick file" />
+
<Switch
android:id="@+id/lightMaterialSwitch"
android:layout_width="wrap_content"
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
index 72b342c54d19..dde245ff9baf 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
@@ -16,6 +16,7 @@
package com.android.test.silkfx.materials
import android.app.Activity
+import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
@@ -46,8 +47,14 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
lateinit var scrimOpacityValue: TextView
lateinit var blurRadiusValue: TextView
lateinit var zoomValue: TextView
+ lateinit var textOverlay: TextView
- lateinit var background: Bitmap
+ var background: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ set(value) {
+ field = value
+ backgroundView.setImageBitmap(background)
+ materialView.backgroundBitmap = background
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -68,10 +75,9 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
scrimOpacityValue = requireViewById(R.id.scrimOpacityValue)
blurRadiusValue = requireViewById(R.id.blurRadiusValue)
zoomValue = requireViewById(R.id.zoomValue)
+ textOverlay = requireViewById(R.id.textOverlay)
background = BitmapFactory.decodeResource(resources, R.drawable.background1)
- backgroundView.setImageBitmap(background)
- materialView.backgroundBitmap = background
blurRadiusSeekBar.setOnSeekBarChangeListener(this)
materialOpacitySeekBar.setOnSeekBarChangeListener(this)
@@ -86,6 +92,7 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
lightMaterialSwitch.setOnCheckedChangeListener { _, isChecked ->
materialView.color = if (isChecked) Color.WHITE else Color.BLACK
+ textOverlay.setTextColor(if (isChecked) Color.BLACK else Color.WHITE)
}
}
@@ -116,6 +123,11 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
}
}
+ override fun onStop() {
+ super.onStop()
+ materialView.resetGyroOffsets()
+ }
+
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
@@ -128,7 +140,23 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
}
background = BitmapFactory.decodeResource(resources, resource)
- backgroundView.setImageBitmap(background)
- materialView.backgroundBitmap = background
+ }
+
+ fun onPickImageClick(view: View) {
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "image/*"
+ }
+ startActivityForResult(intent, 0)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (resultCode === RESULT_OK) {
+ data?.data?.also {
+ contentResolver.openFileDescriptor(it, "r").let {
+ background = BitmapFactory.decodeFileDescriptor(it?.fileDescriptor)
+ }
+ }
+ }
}
} \ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 60797680b79e..711758476a62 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -25,23 +25,83 @@ import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
+import android.graphics.RadialGradient
import android.graphics.Rect
import android.graphics.Shader
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
+import android.widget.FrameLayout
+import com.android.internal.graphics.ColorUtils
import com.android.test.silkfx.R
+import kotlin.math.sin
+import kotlin.math.sqrt
-class GlassView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
+class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(context, attributeSet) {
- var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
- var materialPaint = Paint()
- var scrimPaint = Paint()
- var noisePaint = Paint()
- var blurPaint = Paint()
+ private val textureTranslationMultiplier = 200f
- val src = Rect()
- val dst = Rect()
+ private var gyroXRotation = 0f
+ private var gyroYRotation = 0f
+
+ private var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
+ private var materialPaint = Paint()
+ private var scrimPaint = Paint()
+ private var noisePaint = Paint()
+ private var blurPaint = Paint()
+
+ private val src = Rect()
+ private val dst = Rect()
+
+ private val sensorManager = context.getSystemService(SensorManager::class.java)
+ private val sensorListener = object : SensorEventListener {
+
+ // Constant to convert nanoseconds to seconds.
+ private val NS2S = 1.0f / 1000000000.0f
+ private val EPSILON = 0.000001f
+ private var timestamp: Float = 0f
+
+ override fun onSensorChanged(event: SensorEvent?) {
+ // This timestep's delta rotation to be multiplied by the current rotation
+ // after computing it from the gyro sample data.
+ if (timestamp != 0f && event != null) {
+ val dT = (event.timestamp - timestamp) * NS2S
+ // Axis of the rotation sample, not normalized yet.
+ var axisX: Float = event.values[0]
+ var axisY: Float = event.values[1]
+ var axisZ: Float = event.values[2]
+
+ // Calculate the angular speed of the sample
+ val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)
+
+ // Normalize the rotation vector if it's big enough to get the axis
+ // (that is, EPSILON should represent your maximum allowable margin of error)
+ if (omegaMagnitude > EPSILON) {
+ axisX /= omegaMagnitude
+ axisY /= omegaMagnitude
+ axisZ /= omegaMagnitude
+ }
+
+ // Integrate around this axis with the angular speed by the timestep
+ // in order to get a delta rotation from this sample over the timestep
+ // We will convert this axis-angle representation of the delta rotation
+ // into a quaternion before turning it into the rotation matrix.
+ val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
+ val sinThetaOverTwo: Float = sin(thetaOverTwo)
+ gyroXRotation += sinThetaOverTwo * axisX
+ gyroYRotation += sinThetaOverTwo * axisY
+
+ invalidate()
+ }
+ timestamp = event?.timestamp?.toFloat() ?: 0f
+ }
+
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { }
+ }
var backgroundBitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
set(value) {
@@ -97,6 +157,7 @@ class GlassView(context: Context, attributeSet: AttributeSet) : View(context, at
}
init {
+ setWillNotDraw(false)
materialPaint.blendMode = BlendMode.SOFT_LIGHT
noisePaint.blendMode = BlendMode.SOFT_LIGHT
noisePaint.shader = BitmapShader(noise, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
@@ -112,12 +173,25 @@ class GlassView(context: Context, attributeSet: AttributeSet) : View(context, at
clipToOutline = true
}
+ override fun onAttachedToWindow() {
+ sensorManager?.getSensorList(Sensor.TYPE_GYROSCOPE)?.firstOrNull().let {
+ sensorManager?.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_GAME)
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ sensorManager?.unregisterListener(sensorListener)
+ }
+
override fun onDraw(canvas: Canvas?) {
- src.set(-width/2, -height/2, width/2, height/2)
+ src.set(-width / 2, -height / 2, width / 2, height / 2)
src.scale(1.0f + zoom)
val centerX = left + width / 2
val centerY = top + height / 2
- src.set(src.left + centerX, src.top + centerY, src.right + centerX, src.bottom + centerY)
+ val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
+ val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
+ src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
+ src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
dst.set(0, 0, width, height)
canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
@@ -125,4 +199,10 @@ class GlassView(context: Context, attributeSet: AttributeSet) : View(context, at
canvas?.drawRect(dst, noisePaint)
canvas?.drawRect(dst, scrimPaint)
}
+
+ fun resetGyroOffsets() {
+ gyroXRotation = 0f
+ gyroYRotation = 0f
+ invalidate()
+ }
} \ No newline at end of file