Merge "Rename onSessionVerificationFailure to onSessionValidationFailure"
diff --git a/api/current.txt b/api/current.txt
index 09de005..b70103b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11900,6 +11900,7 @@
field public static final int INVALID_ID = -1; // 0xffffffff
field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+ field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df9db27..bed7b26 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2075,7 +2075,8 @@
STAGED_SESSION_NO_ERROR,
STAGED_SESSION_VERIFICATION_FAILED,
STAGED_SESSION_ACTIVATION_FAILED,
- STAGED_SESSION_UNKNOWN})
+ STAGED_SESSION_UNKNOWN,
+ STAGED_SESSION_OTHER_ERROR})
@Retention(RetentionPolicy.SOURCE)
public @interface StagedSessionErrorCode{}
/**
@@ -2101,6 +2102,12 @@
*/
public static final int STAGED_SESSION_UNKNOWN = 3;
+ /**
+ * Constant indicating that a known error occurred while processing this staged session, but
+ * the error could not be matched to other categories.
+ */
+ public static final int STAGED_SESSION_OTHER_ERROR = 4;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fbe6a50..b0d4497 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -743,6 +743,12 @@
@UnsupportedAppUsage
public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
+ /**
+ * Returns the proportion of power consumed by the System Service
+ * calls made by this UID.
+ */
+ public abstract double getProportionalSystemServiceUsage();
+
public abstract ControllerActivityCounter getWifiControllerActivity();
public abstract ControllerActivityCounter getBluetoothControllerActivity();
public abstract ControllerActivityCounter getModemControllerActivity();
@@ -2882,6 +2888,17 @@
public abstract int getDischargeAmountScreenDozeSinceCharge();
/**
+ * Returns the approximate CPU time (in microseconds) spent by the system server handling
+ * incoming service calls from apps.
+ *
+ * @param cluster the index of the CPU cluster.
+ * @param step the index of the CPU speed. This is not the actual speed of the CPU.
+ * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
+ * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+ */
+ public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step);
+
+ /**
* Returns the total, last, or current battery uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 64ddb2f..3f02d70 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1814,19 +1814,13 @@
/**
* Called after window layout to update the bounds surface. If the surface insets have changed
* or the surface has resized, update the bounds surface.
- *
- * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl.
*/
- private void updateBoundsLayer(boolean shouldReparent) {
+ private void updateBoundsLayer() {
if (mBoundsLayer != null) {
setBoundsLayerCrop();
- mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
- mSurface.getNextFrameNumber());
-
- if (shouldReparent) {
- mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl());
- }
- mTransaction.apply();
+ mTransaction.deferTransactionUntil(mBoundsLayer,
+ getRenderSurfaceControl(), mSurface.getNextFrameNumber())
+ .apply();
}
}
@@ -2905,16 +2899,7 @@
}
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
- // If the surface has been replaced, there's a chance the bounds layer is not parented
- // to the new layer. When updating bounds layer, also reparent to the main VRI
- // SurfaceControl to ensure it's correctly placed in the hierarchy.
- //
- // This needs to be done on the client side since WMS won't reparent the children to the
- // new surface if it thinks the app is closing. WMS gets the signal that the app is
- // stopping, but on the client side it doesn't get stopped since it's restarted quick
- // enough. WMS doesn't want to keep around old children since they will leak when the
- // client creates new children.
- updateBoundsLayer(surfaceReplaced);
+ updateBoundsLayer();
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index e814ec6..eb67191 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -29,7 +29,7 @@
oneway interface IWindowMagnificationConnection {
/**
- * Enables window magnification on specifed display with specified center and scale.
+ * Enables window magnification on specified display with given center and scale and animation.
*
* @param displayId The logical display id.
* @param scale magnification scale.
@@ -41,7 +41,7 @@
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
/**
- * Sets the scale of the window magnifier on specifed display.
+ * Sets the scale of the window magnifier on specified display.
*
* @param displayId The logical display id.
* @param scale magnification scale.
@@ -49,14 +49,14 @@
void setScale(int displayId, float scale);
/**
- * Disables window magnification on specifed display.
+ * Disables window magnification on specified display with animation.
*
* @param displayId The logical display id.
*/
void disableWindowMagnification(int displayId);
/**
- * Moves the window magnifier on the specifed display.
+ * Moves the window magnifier on the specified display. It has no effect while animating.
*
* @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
* current screen pixels.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c4eb396..7683067 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -385,6 +385,7 @@
private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
private boolean mFlagCursorDragFromAnywhereEnabled;
+ private float mCursorDragDirectionMinXYRatio;
private boolean mFlagInsertionHandleGesturesEnabled;
// Specifies whether the new magnifier (with fish-eye effect) is enabled.
@@ -425,6 +426,11 @@
mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting(
WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0;
+ final int cursorDragMinAngleFromVertical = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL,
+ WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT);
+ mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(
+ cursorDragMinAngleFromVertical);
mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting(
WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES,
WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0;
@@ -437,6 +443,8 @@
if (TextView.DEBUG_CURSOR) {
logCursor("Editor", "Cursor drag from anywhere is %s.",
mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled");
+ logCursor("Editor", "Cursor drag min angle from vertical is %d (= %f x/y ratio)",
+ cursorDragMinAngleFromVertical, mCursorDragDirectionMinXYRatio);
logCursor("Editor", "Insertion handle gestures is %s.",
mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled");
logCursor("Editor", "New magnifier is %s.",
@@ -463,6 +471,11 @@
}
@VisibleForTesting
+ public void setCursorDragMinAngleFromVertical(int degreesFromVertical) {
+ mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(degreesFromVertical);
+ }
+
+ @VisibleForTesting
public boolean getFlagInsertionHandleGesturesEnabled() {
return mFlagInsertionHandleGesturesEnabled;
}
@@ -6127,10 +6140,11 @@
if (mIsDraggingCursor) {
performCursorDrag(event);
} else if (mFlagCursorDragFromAnywhereEnabled
- && mTextView.getLayout() != null
- && mTextView.isFocused()
- && mTouchState.isMovedEnoughForDrag()
- && !mTouchState.isDragCloseToVertical()) {
+ && mTextView.getLayout() != null
+ && mTextView.isFocused()
+ && mTouchState.isMovedEnoughForDrag()
+ && (mTouchState.getInitialDragDirectionXYRatio()
+ > mCursorDragDirectionMinXYRatio || mTouchState.isOnHandle())) {
startCursorDrag(event);
}
break;
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index 9eb63087..7514368 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -59,7 +59,7 @@
private boolean mMultiTapInSameArea;
private boolean mMovedEnoughForDrag;
- private boolean mIsDragCloseToVertical;
+ private float mInitialDragDirectionXYRatio;
public float getLastDownX() {
return mLastDownX;
@@ -98,8 +98,23 @@
return mMovedEnoughForDrag;
}
- public boolean isDragCloseToVertical() {
- return mIsDragCloseToVertical && !mIsOnHandle;
+ /**
+ * When {@link #isMovedEnoughForDrag()} is {@code true}, this function returns the x/y ratio for
+ * the initial drag direction. Smaller values indicate that the direction is closer to vertical,
+ * while larger values indicate that the direction is closer to horizontal. For example:
+ * <ul>
+ * <li>if the drag direction is exactly vertical, this returns 0
+ * <li>if the drag direction is exactly horizontal, this returns {@link Float#MAX_VALUE}
+ * <li>if the drag direction is 45 deg from vertical, this returns 1
+ * <li>if the drag direction is 30 deg from vertical, this returns 0.58 (x delta is smaller
+ * than y delta)
+ * <li>if the drag direction is 60 deg from vertical, this returns 1.73 (x delta is bigger
+ * than y delta)
+ * </ul>
+ * This function never returns negative values, regardless of the direction of the drag.
+ */
+ public float getInitialDragDirectionXYRatio() {
+ return mInitialDragDirectionXYRatio;
}
public void setIsOnHandle(boolean onHandle) {
@@ -155,7 +170,7 @@
mLastDownY = event.getY();
mLastDownMillis = event.getEventTime();
mMovedEnoughForDrag = false;
- mIsDragCloseToVertical = false;
+ mInitialDragDirectionXYRatio = 0.0f;
} else if (action == MotionEvent.ACTION_UP) {
if (TextView.DEBUG_CURSOR) {
logCursor("EditorTouchState", "ACTION_UP");
@@ -164,7 +179,7 @@
mLastUpY = event.getY();
mLastUpMillis = event.getEventTime();
mMovedEnoughForDrag = false;
- mIsDragCloseToVertical = false;
+ mInitialDragDirectionXYRatio = 0.0f;
} else if (action == MotionEvent.ACTION_MOVE) {
if (!mMovedEnoughForDrag) {
float deltaX = event.getX() - mLastDownX;
@@ -174,9 +189,8 @@
int touchSlop = config.getScaledTouchSlop();
mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
if (mMovedEnoughForDrag) {
- // If the direction of the swipe motion is within 45 degrees of vertical, it is
- // considered a vertical drag.
- mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
+ mInitialDragDirectionXYRatio = (deltaY == 0) ? Float.MAX_VALUE :
+ Math.abs(deltaX / deltaY);
}
}
} else if (action == MotionEvent.ACTION_CANCEL) {
@@ -185,7 +199,7 @@
mMultiTapStatus = MultiTapStatus.NONE;
mMultiTapInSameArea = false;
mMovedEnoughForDrag = false;
- mIsDragCloseToVertical = false;
+ mInitialDragDirectionXYRatio = 0.0f;
}
}
@@ -201,4 +215,27 @@
float distanceSquared = (deltaX * deltaX) + (deltaY * deltaY);
return distanceSquared <= maxDistance * maxDistance;
}
+
+ /**
+ * Returns the x/y ratio corresponding to the given angle relative to vertical. Smaller angle
+ * values (ie, closer to vertical) will result in a smaller x/y ratio. For example:
+ * <ul>
+ * <li>if the angle is 45 deg, the ratio is 1
+ * <li>if the angle is 30 deg, the ratio is 0.58 (x delta is smaller than y delta)
+ * <li>if the angle is 60 deg, the ratio is 1.73 (x delta is bigger than y delta)
+ * </ul>
+ * If the passed-in value is <= 0, this function returns 0. If the passed-in value is >= 90,
+ * this function returns {@link Float#MAX_VALUE}.
+ *
+ * @see #getInitialDragDirectionXYRatio()
+ */
+ public static float getXYRatio(int angleFromVerticalInDegrees) {
+ if (angleFromVerticalInDegrees <= 0) {
+ return 0.0f;
+ }
+ if (angleFromVerticalInDegrees >= 90) {
+ return Float.MAX_VALUE;
+ }
+ return (float) Math.tan(Math.toRadians(angleFromVerticalInDegrees));
+ }
}
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index 832dd51..1a49365 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -41,6 +41,28 @@
public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
/**
+ * Threshold for the direction of a swipe gesture in order for it to be handled as a cursor drag
+ * rather than a scroll. The direction angle of the swipe gesture must exceed this value in
+ * order to trigger cursor drag; otherwise, the swipe will be assumed to be a scroll gesture.
+ * The value units for this flag is degrees and the valid range is [0,90] inclusive. If a value
+ * < 0 is set, 0 will be used instead; if a value > 90 is set, 90 will be used instead.
+ */
+ public static final String CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL =
+ "CursorControlFeature__min_angle_from_vertical_to_start_cursor_drag";
+
+ /**
+ * The key used in app core settings for the flag
+ * {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}.
+ */
+ public static final String KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL =
+ "widget__min_angle_from_vertical_to_start_cursor_drag";
+
+ /**
+ * Default value for the flag {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}.
+ */
+ public static final int CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT = 45;
+
+ /**
* The flag of finger-to-cursor distance in DP for cursor dragging.
* The value unit is DP and the range is {0..100}. If the value is out of range, the legacy
* value, which is based on handle size, will be used.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3a89dcd..49ad81b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3134,7 +3134,9 @@
// ends up disabled. That's because at some point the old tab's vertical scrolling is
// disabled and the new tab's is enabled. For context, see b/159997845
setVerticalScrollEnabled(true);
- mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
+ if (mResolverDrawerLayout != null) {
+ mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
+ }
}
@Override
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ea3d2de..eb59f0f 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -123,10 +123,15 @@
// Flag related to Privacy Indicators
/**
- * Whether the Permissions Hub is showing.
+ * Whether to show the complete ongoing app ops chip.
*/
public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
+ /**
+ * Whether to show app ops chip for just microphone + camera.
+ */
+ public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";
+
// Flags related to Assistant
/**
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index b3ea118..2620ba0 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -129,6 +129,7 @@
public double videoPowerMah;
public double wakeLockPowerMah;
public double wifiPowerMah;
+ public double systemServiceCpuPowerMah;
// ****************
// This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
@@ -242,6 +243,7 @@
videoPowerMah += other.videoPowerMah;
proportionalSmearMah += other.proportionalSmearMah;
totalSmearedPowerMah += other.totalSmearedPowerMah;
+ systemServiceCpuPowerMah += other.systemServiceCpuPowerMah;
}
/**
@@ -253,7 +255,8 @@
public double sumPower() {
totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
- flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah;
+ flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah
+ + systemServiceCpuPowerMah;
totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
return totalPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b131ab8..3dfa3c3 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -133,6 +133,7 @@
private double mMaxDrainedPower;
PowerCalculator mCpuPowerCalculator;
+ SystemServicePowerCalculator mSystemServicePowerCalculator;
PowerCalculator mWakelockPowerCalculator;
MobileRadioPowerCalculator mMobileRadioPowerCalculator;
PowerCalculator mWifiPowerCalculator;
@@ -396,6 +397,11 @@
}
mCpuPowerCalculator.reset();
+ if (mSystemServicePowerCalculator == null) {
+ mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats);
+ }
+ mSystemServicePowerCalculator.reset();
+
if (mMemoryPowerCalculator == null) {
mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile);
}
@@ -588,6 +594,8 @@
mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
mStatsType);
mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
+ mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
+ mStatsType);
final double totalPower = app.sumPower();
if (DEBUG && totalPower != 0) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 58ba16b..8498151 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -144,6 +144,7 @@
private static final boolean DEBUG = false;
public static final boolean DEBUG_ENERGY = false;
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
+ private static final boolean DEBUG_BINDER_STATS = true;
private static final boolean DEBUG_MEMORY = false;
private static final boolean DEBUG_HISTORY = false;
private static final boolean USE_OLD_HISTORY = false; // for debugging.
@@ -154,7 +155,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -218,10 +219,13 @@
new KernelCpuUidClusterTimeReader(true);
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+ @VisibleForTesting
+ protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
= new KernelMemoryBandwidthStats();
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
+
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
}
@@ -267,6 +271,7 @@
/** Container for Rail Energy Data stats. */
private final RailStats mTmpRailStats = new RailStats();
+
/**
* Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
* {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
@@ -1007,6 +1012,16 @@
private long[] mCpuFreqs;
+ /**
+ * Times spent by the system server threads grouped by cluster and CPU speed.
+ */
+ private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs;
+
+ /**
+ * Times spent by the system server threads handling incoming binder requests.
+ */
+ private LongSamplingCounter[][] mBinderThreadCpuTimesUs;
+
@VisibleForTesting
protected PowerProfile mPowerProfile;
@@ -6131,10 +6146,77 @@
* the power consumption to the calling app.
*/
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
+ Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
synchronized (this) {
getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
callStats);
+ mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
+ }
+ }
+
+ /**
+ * Estimates the proportion of system server CPU activity handling incoming binder calls
+ * that can be attributed to each app
+ */
+ @VisibleForTesting
+ public void updateSystemServiceCallStats() {
+ // Start off by computing the average duration of recorded binder calls,
+ // regardless of which binder or transaction. We will use this as a fallback
+ // for calls that were not sampled at all.
+ int totalRecordedCallCount = 0;
+ long totalRecordedCallTimeMicros = 0;
+ for (int i = 0; i < mUidStats.size(); i++) {
+ Uid uid = mUidStats.valueAt(i);
+ ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
+ for (int j = binderCallStats.size() - 1; j >= 0; j--) {
+ BinderCallStats stats = binderCallStats.valueAt(j);
+ totalRecordedCallCount += stats.recordedCallCount;
+ totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros;
+ }
+ }
+
+ long totalSystemServiceTimeMicros = 0;
+
+ // For every UID, use recorded durations of sampled binder calls to estimate
+ // the total time the system server spent handling requests from this UID.
+ for (int i = 0; i < mUidStats.size(); i++) {
+ Uid uid = mUidStats.valueAt(i);
+
+ long totalTimeForUid = 0;
+ int totalCallCountForUid = 0;
+ ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
+ for (int j = binderCallStats.size() - 1; j >= 0; j--) {
+ BinderCallStats stats = binderCallStats.valueAt(j);
+ totalCallCountForUid += stats.callCount;
+ if (stats.recordedCallCount > 0) {
+ totalTimeForUid +=
+ stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount;
+ } else if (totalRecordedCallCount > 0) {
+ totalTimeForUid +=
+ stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount;
+ }
+ }
+
+ if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) {
+ // Estimate remaining calls, which were not tracked because of binder call
+ // stats sampling
+ totalTimeForUid +=
+ (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros
+ / totalRecordedCallCount;
+ }
+
+ uid.mSystemServiceTimeUs = totalTimeForUid;
+ totalSystemServiceTimeMicros += totalTimeForUid;
+ }
+
+ for (int i = 0; i < mUidStats.size(); i++) {
+ Uid uid = mUidStats.valueAt(i);
+ if (totalSystemServiceTimeMicros > 0) {
+ uid.mProportionalSystemServiceUsage =
+ (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros;
+ } else {
+ uid.mProportionalSystemServiceUsage = 0;
+ }
}
}
@@ -6583,7 +6665,7 @@
/**
* Accumulates stats for a specific binder transaction.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @VisibleForTesting
protected static class BinderCallStats {
static final Comparator<BinderCallStats> COMPARATOR =
Comparator.comparing(BinderCallStats::getClassName)
@@ -6822,6 +6904,16 @@
*/
private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
+ /**
+ * Estimated total time spent by the system server handling requests from this uid.
+ */
+ private long mSystemServiceTimeUs;
+
+ /**
+ * Estimated proportion of system server binder call CPU cost for this uid.
+ */
+ private double mProportionalSystemServiceUsage;
+
public Uid(BatteryStatsImpl bsi, int uid) {
mBsi = bsi;
mUid = uid;
@@ -6899,7 +6991,6 @@
return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
}
-
@Override
public long[] getCpuFreqTimes(int which, int procState) {
if (which < 0 || which >= NUM_PROCESS_STATE) {
@@ -6934,10 +7025,16 @@
return mBinderCallCount;
}
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public ArraySet<BinderCallStats> getBinderCallStats() {
return mBinderCallStats;
}
+ @Override
+ public double getProportionalSystemServiceUsage() {
+ return mProportionalSystemServiceUsage;
+ }
+
public void addIsolatedUid(int isolatedUid) {
if (mChildUids == null) {
mChildUids = new IntArray();
@@ -8029,9 +8126,12 @@
mBinderCallCount = 0;
mBinderCallStats.clear();
+ mProportionalSystemServiceUsage = 0;
+
mLastStepUserTime = mLastStepSystemTime = 0;
mCurStepUserTime = mCurStepSystemTime = 0;
+
return !active;
}
@@ -8373,28 +8473,7 @@
mUserCpuTime.writeToParcel(out);
mSystemCpuTime.writeToParcel(out);
- if (mCpuClusterSpeedTimesUs != null) {
- out.writeInt(1);
- out.writeInt(mCpuClusterSpeedTimesUs.length);
- for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
- if (cpuSpeeds != null) {
- out.writeInt(1);
- out.writeInt(cpuSpeeds.length);
- for (LongSamplingCounter c : cpuSpeeds) {
- if (c != null) {
- out.writeInt(1);
- c.writeToParcel(out);
- } else {
- out.writeInt(0);
- }
- }
- } else {
- out.writeInt(0);
- }
- }
- } else {
- out.writeInt(0);
- }
+ mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs);
LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
@@ -8432,6 +8511,7 @@
} else {
out.writeInt(0);
}
+ out.writeDouble(mProportionalSystemServiceUsage);
}
void readJobCompletionsFromParcelLocked(Parcel in) {
@@ -8692,36 +8772,7 @@
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
- if (in.readInt() != 0) {
- int numCpuClusters = in.readInt();
- if (mBsi.mPowerProfile != null && mBsi.mPowerProfile.getNumCpuClusters() != numCpuClusters) {
- throw new ParcelFormatException("Incompatible number of cpu clusters");
- }
-
- mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][];
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- if (in.readInt() != 0) {
- int numSpeeds = in.readInt();
- if (mBsi.mPowerProfile != null &&
- mBsi.mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
- throw new ParcelFormatException("Incompatible number of cpu speeds");
- }
-
- final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
- mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds;
- for (int speed = 0; speed < numSpeeds; speed++) {
- if (in.readInt() != 0) {
- cpuSpeeds[speed] = new LongSamplingCounter(
- mBsi.mOnBatteryTimeBase, in);
- }
- }
- } else {
- mCpuClusterSpeedTimesUs[cluster] = null;
- }
- }
- } else {
- mCpuClusterSpeedTimesUs = null;
- }
+ mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in);
mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase);
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
@@ -8762,6 +8813,8 @@
} else {
mWifiRadioApWakeupCount = null;
}
+
+ mProportionalSystemServiceUsage = in.readDouble();
}
public void noteJobsDeferredLocked(int numDeferred, long sinceLast) {
@@ -9904,7 +9957,6 @@
UserInfoProvider userInfoProvider) {
init(clocks);
-
if (systemDir == null) {
mStatsFile = null;
mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
@@ -10046,6 +10098,8 @@
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
+ mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
+
if (mEstimatedBatteryCapacity == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
@@ -10726,6 +10780,9 @@
mTmpRailStats.reset();
+ resetIfNotNull(mSystemServerThreadCpuTimesUs, false);
+ resetIfNotNull(mBinderThreadCpuTimesUs, false);
+
mLastHistoryStepDetails = null;
mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
@@ -10853,7 +10910,7 @@
return null;
}
- /**
+ /**
* Distribute WiFi energy info and network traffic to apps.
* @param info The energy information from the WiFi controller.
*/
@@ -11772,6 +11829,7 @@
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
mKernelCpuSpeedReaders[cluster].readDelta();
}
+ mSystemServerCpuThreadReader.readDelta();
return;
}
@@ -11791,6 +11849,87 @@
readKernelUidCpuClusterTimesLocked(onBattery);
mNumAllUidCpuTimeReads += 2;
}
+
+ updateSystemServerThreadStats();
+ }
+
+ /**
+ * Estimates the proportion of the System Server CPU activity (per cluster per speed)
+ * spent on handling incoming binder calls.
+ */
+ @VisibleForTesting
+ public void updateSystemServerThreadStats() {
+ // There are some simplifying assumptions made in this algorithm
+ // 1) We assume that if a thread handles incoming binder calls, all of its activity
+ // is spent doing that. Most incoming calls are handled by threads allocated
+ // by the native layer in the binder thread pool, so this assumption is reasonable.
+ // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU
+ // cost. In reality, in multi-core CPUs, the CPU cost may not be linearly
+ // affected by additional threads.
+
+ SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+ mSystemServerCpuThreadReader.readDelta();
+
+ int index = 0;
+ int numCpuClusters = mPowerProfile.getNumCpuClusters();
+ if (mSystemServerThreadCpuTimesUs == null) {
+ mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+ mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+ }
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ if (mSystemServerThreadCpuTimesUs[cluster] == null) {
+ mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
+ mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ mSystemServerThreadCpuTimesUs[cluster][speed] =
+ new LongSamplingCounter(mOnBatteryTimeBase);
+ mBinderThreadCpuTimesUs[cluster][speed] =
+ new LongSamplingCounter(mOnBatteryTimeBase);
+ }
+ }
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked(
+ systemServiceCpuThreadTimes.threadCpuTimesUs[index]);
+ mBinderThreadCpuTimesUs[cluster][speed].addCountLocked(
+ systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]);
+ index++;
+ }
+ }
+ if (DEBUG_BINDER_STATS) {
+ Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
+ long binderThreadTime = 0;
+ long totalThreadTime = 0;
+ int cpuIndex = 0;
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("cpu").append(cpuIndex).append(": [");
+ int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ if (speed != 0) {
+ sb.append(", ");
+ }
+ long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(
+ 0) / 1000;
+ long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
+ / 1000;
+ sb.append(String.format("%d/%d(%.1f%%)",
+ binderCount,
+ totalCount,
+ totalCount != 0 ? (double) binderCount * 100 / totalCount : 0));
+
+ totalThreadTime += totalCount;
+ binderThreadTime += binderCount;
+ index++;
+ }
+ cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
+ Slog.d(TAG, sb.toString());
+ }
+ Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime);
+ Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
+ binderThreadTime,
+ binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0));
+ }
}
/**
@@ -12998,6 +13137,75 @@
}
}
+
+ @Override
+ public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) {
+ // Estimates the time spent by the system server handling incoming binder requests.
+ //
+ // The data that we can get from the kernel is this:
+ // - CPU duration for a (thread - cluster - CPU speed) combination
+ // - CPU duration for a (UID - cluster - CPU speed) combination
+ //
+ // The configuration we have in the Power Profile is this:
+ // - Average CPU power for a (cluster - CPU speed) combination.
+ //
+ // The model used by BatteryStats can be illustrated with this example:
+ //
+ // - Let's say the system server has 10 threads.
+ // - These 10 threads spent 1000 ms of CPU time in aggregate
+ // - Of the 10 threads 4 were execute exclusively incoming binder calls.
+ // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
+ // - The real time spent by the system server UID doing all of this is, say, 200 ms.
+ //
+ // We will assume that power consumption is proportional to the time spent by the CPU
+ // across all threads. This is a crude assumption, but we don't have more detailed data.
+ // Thus,
+ // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime
+ //
+ // In our example,
+ // binderRealTime = 200 * 600 / 1000 = 120ms
+ //
+ // We can then multiply this estimated time by the average power to obtain an estimate
+ // of the total power consumed by incoming binder calls for the given cluster/speed
+ // combination.
+
+ if (mSystemServerThreadCpuTimesUs == null) {
+ return 0;
+ }
+
+ if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) {
+ return 0;
+ }
+
+ final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster];
+
+ if (step < 0 || step >= threadTimesForCluster.length) {
+ return 0;
+ }
+
+ Uid systemUid = mUidStats.get(Process.SYSTEM_UID);
+ if (systemUid == null) {
+ return 0;
+ }
+
+ final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step,
+ BatteryStats.STATS_SINCE_CHARGED);
+ if (uidTimeAtCpuSpeed == 0) {
+ return 0;
+ }
+
+ final long uidThreadTime =
+ threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+
+ if (uidThreadTime == 0) {
+ return 0;
+ }
+
+ final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
+ BatteryStats.STATS_SINCE_CHARGED);
+ return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime;
+ }
+
/**
* Retrieve the statistics object for a particular uid, creating if needed.
*/
@@ -13327,45 +13535,7 @@
pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
}
- pw.println("Per UID system service calls:");
- BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver();
- for (int i = 0; i < size; i++) {
- int u = mUidStats.keyAt(i);
- Uid uid = mUidStats.get(u);
- long binderCallCount = uid.getBinderCallCount();
- if (binderCallCount != 0) {
- pw.print(" ");
- pw.print(u);
- pw.print(" system service calls: ");
- pw.print(binderCallCount);
- ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats();
- if (!binderCallStats.isEmpty()) {
- pw.println(", including");
- BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()];
- binderCallStats.toArray(bcss);
- for (BinderCallStats bcs : bcss) {
- bcs.ensureMethodName(nameResolver);
- }
- Arrays.sort(bcss, BinderCallStats.COMPARATOR);
- for (BinderCallStats callStats : bcss) {
- pw.print(" ");
- pw.print(callStats.getClassName());
- pw.print('#');
- pw.print(callStats.getMethodName());
- pw.print(" calls: ");
- pw.print(callStats.callCount);
- if (callStats.recordedCallCount != 0) {
- pw.print(" time: ");
- pw.print(callStats.callCount * callStats.recordedCpuTimeMicros
- / callStats.recordedCallCount / 1000);
- }
- pw.println();
- }
- } else {
- pw.println();
- }
- }
- }
+
pw.println("Per UID CPU active time in ms:");
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
@@ -13390,6 +13560,30 @@
pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
}
}
+
+ updateSystemServiceCallStats();
+ if (mSystemServerThreadCpuTimesUs != null) {
+ pw.println("Per UID System server binder time in ms:");
+ for (int i = 0; i < size; i++) {
+ int u = mUidStats.keyAt(i);
+ Uid uid = mUidStats.get(u);
+ double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
+
+ long time = 0;
+ for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) {
+ int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length;
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ time += getSystemServiceTimeAtCpuSpeed(cluster, speed)
+ * proportionalSystemServiceUsage;
+ }
+ }
+
+ pw.print(" ");
+ pw.print(u);
+ pw.print(": ");
+ pw.println(time);
+ }
+ }
}
final ReentrantLock mWriteLock = new ReentrantLock();
@@ -14908,6 +15102,9 @@
u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in);
mUidStats.append(uid, u);
}
+
+ mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
+ mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
}
public void writeToParcel(Parcel out, int flags) {
@@ -14923,6 +15120,8 @@
// Need to update with current kernel wake lock counts.
pullPendingStateUpdatesLocked();
+ updateSystemServiceCallStats();
+
// Pull the clock time. This may update the time and make a new history entry
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
@@ -15105,6 +15304,73 @@
} else {
out.writeInt(0);
}
+ writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs);
+ writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs);
+ }
+
+ private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) {
+ if (counters == null) {
+ out.writeInt(0);
+ return;
+ }
+
+ out.writeInt(1);
+ out.writeInt(counters.length);
+ for (int i = 0; i < counters.length; i++) {
+ LongSamplingCounter[] counterArray = counters[i];
+ if (counterArray == null) {
+ out.writeInt(0);
+ continue;
+ }
+
+ out.writeInt(1);
+ out.writeInt(counterArray.length);
+ for (int j = 0; j < counterArray.length; j++) {
+ LongSamplingCounter c = counterArray[j];
+ if (c != null) {
+ out.writeInt(1);
+ c.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ }
+ }
+
+ private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) {
+ LongSamplingCounter[][] counters;
+ if (in.readInt() != 0) {
+ int numCpuClusters = in.readInt();
+ if (mPowerProfile != null
+ && mPowerProfile.getNumCpuClusters() != numCpuClusters) {
+ throw new ParcelFormatException("Incompatible number of cpu clusters");
+ }
+
+ counters = new LongSamplingCounter[numCpuClusters][];
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ if (in.readInt() != 0) {
+ int numSpeeds = in.readInt();
+ if (mPowerProfile != null
+ && mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
+ throw new ParcelFormatException("Incompatible number of cpu speeds");
+ }
+
+ final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
+ counters[cluster] = cpuSpeeds;
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ if (in.readInt() != 0) {
+ cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ }
+ }
+ } else {
+ counters[cluster] = null;
+ }
+ }
+ } else {
+ counters = null;
+ }
+
+ return counters;
}
@UnsupportedAppUsage
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index e09ef49..201626a 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -115,7 +115,8 @@
if (uidEntry != null) {
ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
- uidEntry.incrementalCallCount, callStats.values());
+ uidEntry.incrementalCallCount, callStats.values(),
+ mNativeTids.toArray());
uidEntry.incrementalCallCount = 0;
for (int j = callStats.size() - 1; j >= 0; j--) {
callStats.valueAt(j).incrementalCallCount = 0;
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index feb5aab..f14d5f2 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -142,7 +142,8 @@
* Notes incoming binder call stats associated with this work source UID.
*/
void noteCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats);
+ Collection<BinderCallsStats.CallStat> callStats,
+ int[] binderThreadNativeTids);
}
/**
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3407670..2ba372a 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -225,19 +225,22 @@
/** Set the number of frequency buckets to use */
void setNumBuckets(int numBuckets) {
- if (numBuckets < 1) {
- Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
- return;
- }
// If `numBuckets` hasn't changed since the last set, do nothing
if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
return;
}
- mFrequencyBucketCreator =
- new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
- mFrequenciesKhz =
- mFrequencyBucketCreator.bucketFrequencies(
- mProcTimeInStateReader.getFrequenciesKhz());
+
+ final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
+ if (numBuckets != 0) {
+ mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, numBuckets);
+ mFrequenciesKhz = mFrequencyBucketCreator.bucketFrequencies(frequenciesKhz);
+ } else {
+ mFrequencyBucketCreator = null;
+ mFrequenciesKhz = new int[frequenciesKhz.length];
+ for (int i = 0; i < frequenciesKhz.length; i++) {
+ mFrequenciesKhz[i] = (int) frequenciesKhz[i];
+ }
+ }
}
/** Set the UID predicate for {@link #getProcessCpuUsage} */
@@ -320,8 +323,15 @@
if (cpuUsagesLong == null) {
return null;
}
- int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
-
+ final int[] cpuUsages;
+ if (mFrequencyBucketCreator != null) {
+ cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
+ } else {
+ cpuUsages = new int[cpuUsagesLong.length];
+ for (int i = 0; i < cpuUsagesLong.length; i++) {
+ cpuUsages[i] = (int) cpuUsagesLong[i];
+ }
+ }
return new ThreadCpuUsage(threadId, threadName, cpuUsages);
}
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
new file mode 100644
index 0000000..1cdd42c
--- /dev/null
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -0,0 +1,151 @@
+/*
+ * 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.internal.os;
+
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
+ * by various threads of the System Server.
+ */
+public class SystemServerCpuThreadReader {
+ private KernelCpuThreadReader mKernelCpuThreadReader;
+ private int[] mBinderThreadNativeTids;
+
+ private int[] mThreadCpuTimesUs;
+ private int[] mBinderThreadCpuTimesUs;
+ private long[] mLastThreadCpuTimesUs;
+ private long[] mLastBinderThreadCpuTimesUs;
+
+ /**
+ * Times (in microseconds) spent by the system server UID.
+ */
+ public static class SystemServiceCpuThreadTimes {
+ // All threads
+ public long[] threadCpuTimesUs;
+ // Just the threads handling incoming binder calls
+ public long[] binderThreadCpuTimesUs;
+ }
+
+ private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes();
+
+ /**
+ * Creates a configured instance of SystemServerCpuThreadReader.
+ */
+ public static SystemServerCpuThreadReader create() {
+ return new SystemServerCpuThreadReader(
+ KernelCpuThreadReader.create(0, uid -> uid == Process.myUid()));
+ }
+
+ @VisibleForTesting
+ public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException {
+ this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null,
+ new KernelCpuThreadReader.Injector() {
+ @Override
+ public int getUidForPid(int pid) {
+ return systemServerUid;
+ }
+ }));
+ }
+
+ @VisibleForTesting
+ public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) {
+ mKernelCpuThreadReader = kernelCpuThreadReader;
+ }
+
+ public void setBinderThreadNativeTids(int[] nativeTids) {
+ mBinderThreadNativeTids = nativeTids;
+ }
+
+ /**
+ * Returns delta of CPU times, per thread, since the previous call to this method.
+ */
+ public SystemServiceCpuThreadTimes readDelta() {
+ if (mBinderThreadCpuTimesUs == null) {
+ int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length;
+ mThreadCpuTimesUs = new int[numCpuFrequencies];
+ mBinderThreadCpuTimesUs = new int[numCpuFrequencies];
+
+ mLastThreadCpuTimesUs = new long[numCpuFrequencies];
+ mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
+
+ mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
+ mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
+ }
+
+ Arrays.fill(mThreadCpuTimesUs, 0);
+ Arrays.fill(mBinderThreadCpuTimesUs, 0);
+
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage =
+ mKernelCpuThreadReader.getProcessCpuUsage();
+ int processCpuUsageSize = processCpuUsage.size();
+ for (int i = 0; i < processCpuUsageSize; i++) {
+ KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i);
+ ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages;
+ if (threadCpuUsages != null) {
+ int threadCpuUsagesSize = threadCpuUsages.size();
+ for (int j = 0; j < threadCpuUsagesSize; j++) {
+ KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
+ boolean isBinderThread = isBinderThread(tcu.threadId);
+
+ final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
+ for (int k = 0; k < len; k++) {
+ int usageTimeUs = tcu.usageTimesMillis[k] * 1000;
+ mThreadCpuTimesUs[k] += usageTimeUs;
+ if (isBinderThread) {
+ mBinderThreadCpuTimesUs[k] += usageTimeUs;
+ }
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < mThreadCpuTimesUs.length; i++) {
+ if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) {
+ mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i];
+ mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+ } else {
+ mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
+ mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i];
+ mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
+ mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i];
+ }
+ mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i];
+ mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+ }
+
+ return mDeltaCpuThreadTimes;
+ }
+
+ private boolean isBinderThread(int threadId) {
+ if (mBinderThreadNativeTids != null) {
+ for (int i = 0; i < mBinderThreadNativeTids.length; i++) {
+ if (threadId == mBinderThreadNativeTids[i]) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
new file mode 100644
index 0000000..481b901
--- /dev/null
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.internal.os;
+
+import android.os.BatteryStats;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Estimates the amount of power consumed by the System Server handling requests from
+ * a given app.
+ */
+public class SystemServicePowerCalculator extends PowerCalculator {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SystemServicePowerCalc";
+
+ private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
+
+ private final PowerProfile mPowerProfile;
+ private final BatteryStats mBatteryStats;
+ // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds
+ private double[][] mSystemServicePowerMaUs;
+
+ public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) {
+ mPowerProfile = powerProfile;
+ mBatteryStats = batteryStats;
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final double proportionalUsage = u.getProportionalSystemServiceUsage();
+ if (proportionalUsage > 0) {
+ if (mSystemServicePowerMaUs == null) {
+ updateSystemServicePower();
+ }
+
+ double cpuPowerMaUs = 0;
+ int numCpuClusters = mPowerProfile.getNumCpuClusters();
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage;
+ }
+ }
+
+ app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+ }
+ }
+
+ private void updateSystemServicePower() {
+ final int numCpuClusters = mPowerProfile.getNumCpuClusters();
+ mSystemServicePowerMaUs = new double[numCpuClusters][];
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ mSystemServicePowerMaUs[cluster] = new double[numSpeeds];
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ mSystemServicePowerMaUs[cluster][speed] =
+ mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed)
+ * mPowerProfile.getAveragePowerForCpuCore(cluster, speed);
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "System service power per CPU cluster and frequency");
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ Log.d(TAG, "Cluster[" + cluster + "]: "
+ + Arrays.toString(mSystemServicePowerMaUs[cluster]));
+ }
+ }
+ }
+
+ @Override
+ public void reset() {
+ mSystemServicePowerMaUs = null;
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index df2946c..c37a34a 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -201,6 +201,38 @@
}
@Test
+ public void testCursorDrag_diagonal_thresholdConfig() throws Throwable {
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 1; i <= 9; i++) {
+ sb.append("here is some text").append(i).append("\n");
+ }
+ sb.append(Strings.repeat("abcdefghij\n", 400)).append("Last");
+ String text = sb.toString();
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ int index = text.indexOf("text9");
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+
+ // Configure the drag direction threshold to require the drag to be exactly horizontal. With
+ // this set, a swipe that is slightly off horizontal should not trigger cursor drag.
+ editor.setCursorDragMinAngleFromVertical(90);
+ int startIdx = text.indexOf("5");
+ int endIdx = text.indexOf("here is some text3");
+ onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+
+ // Configure the drag direction threshold to require the drag to be 45 degrees or more from
+ // vertical. With this set, the same swipe gesture as above should now trigger cursor drag.
+ editor.setCursorDragMinAngleFromVertical(45);
+ onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(endIdx));
+ }
+
+ @Test
public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable {
String text = "012345_aaa\n"
+ "0123456789\n"
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 35fd4bd..94f43de 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -165,7 +165,7 @@
long event2Time = 1001;
MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
mTouchState.update(event2, mConfig);
- assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+ assertDrag(mTouchState, 20f, 30f, 0, 0, 180f);
// Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
long event3Time = 5000;
@@ -280,7 +280,7 @@
long event3Time = 1002;
MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY);
mTouchState.update(event3, mConfig);
- assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+ assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE);
// Simulate an ACTION_UP event.
long event4Time = 1003;
@@ -301,15 +301,15 @@
long event2Time = 1002;
MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f);
mTouchState.update(event2, mConfig);
- assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+ assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f);
// Simulate another ACTION_MOVE event that is horizontal from the original down event.
- // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
- // initial direction of movement.
+ // The drag direction ratio should NOT change since it should only reflect the initial
+ // direction of movement.
long event3Time = 1003;
MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f);
mTouchState.update(event3, mConfig);
- assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+ assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f);
// Simulate an ACTION_UP event.
long event4Time = 1004;
@@ -330,15 +330,15 @@
long event2Time = 1002;
MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
mTouchState.update(event2, mConfig);
- assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+ assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f);
// Simulate another ACTION_MOVE event that is vertical from the original down event.
- // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
- // initial direction of movement.
+ // The drag direction ratio should NOT change since it should only reflect the initial
+ // direction of movement.
long event3Time = 1003;
MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f);
mTouchState.update(event3, mConfig);
- assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+ assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f);
// Simulate an ACTION_UP event.
long event4Time = 1004;
@@ -374,7 +374,7 @@
long event2Time = 1002;
MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f);
mTouchState.update(event2, mConfig);
- assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+ assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE);
// Simulate an ACTION_CANCEL event.
long event3Time = 1003;
@@ -411,6 +411,84 @@
assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
}
+ @Test
+ public void testGetXYRatio() throws Exception {
+ doTestGetXYRatio(-1, 0.0f);
+ doTestGetXYRatio(0, 0.0f);
+ doTestGetXYRatio(30, 0.58f);
+ doTestGetXYRatio(45, 1.0f);
+ doTestGetXYRatio(60, 1.73f);
+ doTestGetXYRatio(90, Float.MAX_VALUE);
+ doTestGetXYRatio(91, Float.MAX_VALUE);
+ }
+
+ private void doTestGetXYRatio(int angleFromVerticalInDegrees, float expectedXYRatioRounded) {
+ float result = EditorTouchState.getXYRatio(angleFromVerticalInDegrees);
+ String msg = String.format(
+ "%d deg should give an x/y ratio of %f; actual unrounded result is %f",
+ angleFromVerticalInDegrees, expectedXYRatioRounded, result);
+ float roundedResult = (result == 0.0f || result == Float.MAX_VALUE) ? result :
+ Math.round(result * 100) / 100f;
+ assertThat(msg, roundedResult, is(expectedXYRatioRounded));
+ }
+
+ @Test
+ public void testUpdate_dragDirection() throws Exception {
+ // Simulate moving straight up.
+ doTestDragDirection(100f, 100f, 100f, 50f, 0f);
+
+ // Simulate moving straight down.
+ doTestDragDirection(100f, 100f, 100f, 150f, 0f);
+
+ // Simulate moving straight left.
+ doTestDragDirection(100f, 100f, 50f, 100f, Float.MAX_VALUE);
+
+ // Simulate moving straight right.
+ doTestDragDirection(100f, 100f, 150f, 100f, Float.MAX_VALUE);
+
+ // Simulate moving up and right, < 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 110f, 50f, 10f / 50f);
+
+ // Simulate moving up and right, > 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 150f, 90f, 50f / 10f);
+
+ // Simulate moving down and right, < 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 110f, 150f, 10f / 50f);
+
+ // Simulate moving down and right, > 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 150f, 110f, 50f / 10f);
+
+ // Simulate moving down and left, < 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 90f, 150f, 10f / 50f);
+
+ // Simulate moving down and left, > 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 50f, 110f, 50f / 10f);
+
+ // Simulate moving up and left, < 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 90f, 50f, 10f / 50f);
+
+ // Simulate moving up and left, > 45 deg from vertical.
+ doTestDragDirection(100f, 100f, 50f, 90f, 50f / 10f);
+ }
+
+ private void doTestDragDirection(float downX, float downY, float moveX, float moveY,
+ float expectedInitialDragDirectionXYRatio) {
+ EditorTouchState touchState = new EditorTouchState();
+
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1001;
+ MotionEvent event1 = downEvent(event1Time, event1Time, downX, downY);
+ touchState.update(event1, mConfig);
+
+ // Simulate an ACTION_MOVE event.
+ long event2Time = 1002;
+ MotionEvent event2 = moveEvent(event1Time, event2Time, moveX, moveY);
+ touchState.update(event2, mConfig);
+ String msg = String.format("(%.0f,%.0f)=>(%.0f,%.0f)", downX, downY, moveX, moveY);
+ assertThat(msg, touchState.getInitialDragDirectionXYRatio(),
+ is(expectedInitialDragDirectionXYRatio));
+ }
+
private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
}
@@ -441,7 +519,7 @@
}
private static void assertDrag(EditorTouchState touchState, float lastDownX,
- float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) {
+ float lastDownY, float lastUpX, float lastUpY, float initialDragDirectionXYRatio) {
assertThat(touchState.getLastDownX(), is(lastDownX));
assertThat(touchState.getLastDownY(), is(lastDownY));
assertThat(touchState.getLastUpX(), is(lastUpX));
@@ -451,7 +529,7 @@
assertThat(touchState.isMultiTap(), is(false));
assertThat(touchState.isMultiTapInSameArea(), is(false));
assertThat(touchState.isMovedEnoughForDrag(), is(true));
- assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical));
+ assertThat(touchState.getInitialDragDirectionXYRatio(), is(initialDragDirectionXYRatio));
}
private static void assertMultiTap(EditorTouchState touchState,
@@ -467,6 +545,5 @@
|| multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea));
assertThat(touchState.isMovedEnoughForDrag(), is(false));
- assertThat(touchState.isDragCloseToVertical(), is(false));
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
index 3e67b8b..22c41f3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -38,7 +38,8 @@
@SmallTest
public class BatteryStatsBinderCallStatsTest extends TestCase {
- private static final int TRANSACTION_CODE = 100;
+ private static final int TRANSACTION_CODE1 = 100;
+ private static final int TRANSACTION_CODE2 = 101;
/**
* Test BatteryStatsImpl.Uid.noteBinderCallStats.
@@ -53,23 +54,23 @@
Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+ MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
stat1.incrementalCallCount = 21;
stat1.recordedCallCount = 5;
stat1.cpuTimeMicros = 1000;
callStats.add(stat1);
- bi.noteBinderCallStats(workSourceUid, 42, callStats);
+ bi.noteBinderCallStats(workSourceUid, 42, callStats, null);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
- MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+ MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
stat2.incrementalCallCount = 9;
stat2.recordedCallCount = 8;
stat2.cpuTimeMicros = 500;
callStats.add(stat2);
- bi.noteBinderCallStats(workSourceUid, 8, callStats);
+ bi.noteBinderCallStats(workSourceUid, 8, callStats, null);
BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
assertEquals(42 + 8, uid.getBinderCallCount());
@@ -85,9 +86,62 @@
assertEquals(500, value.recordedCpuTimeMicros);
}
+
+ @Test
+ public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ int callingUid = Process.FIRST_APPLICATION_UID + 1;
+ int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1;
+ int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2;
+ int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3;
+
+ Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+ BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid,
+ MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
+ stat1a.incrementalCallCount = 10;
+ stat1a.recordedCallCount = 5;
+ stat1a.cpuTimeMicros = 1000;
+ callStats.add(stat1a);
+
+ BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid,
+ MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */);
+ stat1b.incrementalCallCount = 30;
+ stat1b.recordedCallCount = 15;
+ stat1b.cpuTimeMicros = 1500;
+ callStats.add(stat1b);
+
+ bi.noteBinderCallStats(workSourceUid1, 65, callStats, null);
+
+ // No recorded stats for some methods. Must use the global average.
+ callStats.clear();
+ BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
+ MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
+ stat2.incrementalCallCount = 10;
+ callStats.add(stat2);
+
+ bi.noteBinderCallStats(workSourceUid2, 40, callStats, null);
+
+ // No stats for any calls. Must use the global average
+ callStats.clear();
+ bi.noteBinderCallStats(workSourceUid3, 50, callStats, null);
+
+ bi.updateSystemServiceCallStats();
+
+ double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage();
+ double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage();
+ double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage();
+
+ assertEquals(0.419, prop1, 0.01);
+ assertEquals(0.258, prop2, 0.01);
+ assertEquals(0.323, prop3, 0.01);
+ assertEquals(1.000, prop1 + prop2 + prop3, 0.01);
+ }
+
private static class MockBinder extends Binder {
public static String getDefaultTransactionName(int txCode) {
- return txCode == TRANSACTION_CODE ? "testMethod" : "unknown";
+ return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown";
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index a5117a3..188ba9e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -768,8 +768,8 @@
final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
bcs.setCallStatsObserver(
- (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll(
- callStats));
+ (workSourceUid, incrementalCallCount, callStats, binderThreadIds) ->
+ callStatsList.addAll(callStats));
Binder binder = new Binder();
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bc0e0a4..75dd7fb 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -133,6 +133,12 @@
return this;
}
+ public MockBatteryStatsImpl setSystemServerCpuThreadReader(
+ SystemServerCpuThreadReader systemServerCpuThreadReader) {
+ mSystemServerCpuThreadReader = systemServerCpuThreadReader;
+ return this;
+ }
+
public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) {
mUserInfoProvider = provider;
return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
new file mode 100644
index 0000000..10ba548
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemServerCpuThreadReaderTest {
+ private File mProcDirectory;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getContext();
+ mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mProcDirectory);
+ }
+
+ @Test
+ public void testReaderDelta_firstTime() throws IOException {
+ int uid = 42;
+ setupDirectory(
+ mProcDirectory.toPath().resolve(String.valueOf(uid)),
+ new int[]{42, 1, 2, 3},
+ new int[]{1000, 2000},
+ // Units are 10ms aka 10000Us
+ new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+
+ SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
+ mProcDirectory.toPath(), uid);
+ reader.setBinderThreadNativeTids(new int[]{1, 3});
+ SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+ reader.readDelta();
+ assertArrayEquals(new long[]{100 * 10000, 1100 * 10000},
+ systemServiceCpuThreadTimes.threadCpuTimesUs);
+ assertArrayEquals(new long[]{0, 600 * 10000},
+ systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+ }
+
+ @Test
+ public void testReaderDelta_nextTime() throws IOException {
+ int uid = 42;
+ setupDirectory(
+ mProcDirectory.toPath().resolve(String.valueOf(uid)),
+ new int[]{42, 1, 2, 3},
+ new int[]{1000, 2000},
+ new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+
+ SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
+ mProcDirectory.toPath(), uid);
+ reader.setBinderThreadNativeTids(new int[]{1, 3});
+
+ // First time, populate "last" snapshot
+ reader.readDelta();
+
+ FileUtils.deleteContents(mProcDirectory);
+ setupDirectory(
+ mProcDirectory.toPath().resolve(String.valueOf(uid)),
+ new int[]{42, 1, 2, 3},
+ new int[]{1000, 2000},
+ new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}});
+
+ // Second time, get the actual delta
+ SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+ reader.readDelta();
+
+ assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000},
+ systemServiceCpuThreadTimes.threadCpuTimesUs);
+ assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000},
+ systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+ }
+
+ private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies,
+ int[][] cpuTimes) throws IOException {
+ // Make /proc/$PID
+ assertTrue(processPath.toFile().mkdirs());
+
+ // Make /proc/$PID/task
+ final Path selfThreadsPath = processPath.resolve("task");
+ assertTrue(selfThreadsPath.toFile().mkdirs());
+
+ // Make thread directories in reverse order, as they are read in order of creation by
+ // CpuThreadProcReader
+ for (int i = 0; i < threadIds.length; i++) {
+ // Make /proc/$PID/task/$TID
+ final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
+ assertTrue(threadPath.toFile().mkdirs());
+
+ // Make /proc/$PID/task/$TID/time_in_state
+ final OutputStream timeInStateStream =
+ Files.newOutputStream(threadPath.resolve("time_in_state"));
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ timeInStateStream.close();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
new file mode 100644
index 0000000..ac5443e
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Process;
+
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemServicePowerCalculatorTest {
+
+ private PowerProfile mProfile;
+ private MockBatteryStatsImpl mMockBatteryStats;
+ private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
+ private MockServerCpuThreadReader mMockServerCpuThreadReader;
+ private SystemServicePowerCalculator mSystemServicePowerCalculator;
+
+ @Before
+ public void setUp() throws IOException {
+ Context context = InstrumentationRegistry.getContext();
+ mProfile = new PowerProfile(context, true /* forTest */);
+ mMockServerCpuThreadReader = new MockServerCpuThreadReader();
+ mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
+ mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks())
+ .setPowerProfile(mProfile)
+ .setSystemServerCpuThreadReader(mMockServerCpuThreadReader)
+ .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
+ .setUserInfoProvider(new MockUserInfoProvider());
+ mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
+ mSystemServicePowerCalculator =
+ new SystemServicePowerCalculator(mProfile, mMockBatteryStats);
+ }
+
+ @Test
+ public void testCalculateApp() {
+ // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
+ mMockServerCpuThreadReader.setThreadTimes(
+ new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000},
+ new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000});
+
+ mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
+ new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000}
+ );
+
+ mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false);
+
+ int workSourceUid1 = 100;
+ int workSourceUid2 = 200;
+ int transactionCode = 42;
+
+ Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+ BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1,
+ Binder.class, transactionCode, true /*screenInteractive */);
+ stat1.incrementalCallCount = 100;
+ stat1.recordedCallCount = 100;
+ stat1.cpuTimeMicros = 1000000;
+ callStats.add(stat1);
+
+ mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null);
+
+ callStats.clear();
+ BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
+ Binder.class, transactionCode, true /*screenInteractive */);
+ stat2.incrementalCallCount = 100;
+ stat2.recordedCallCount = 100;
+ stat2.cpuTimeMicros = 9000000;
+ callStats.add(stat2);
+
+ mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null);
+
+ mMockBatteryStats.updateSystemServiceCallStats();
+ mMockBatteryStats.updateSystemServerThreadStats();
+
+ BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP,
+ mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0);
+ mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0,
+ BatteryStats.STATS_SINCE_CHARGED);
+ assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001);
+
+ BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP,
+ mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0);
+ mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0,
+ BatteryStats.STATS_SINCE_CHARGED);
+ assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001);
+ }
+
+ private static class MockKernelCpuUidFreqTimeReader extends
+ KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader {
+ private long[] mSystemServerCpuTimes;
+
+ MockKernelCpuUidFreqTimeReader() {
+ super(/*throttle */false);
+ }
+
+ void setSystemServerCpuTimes(long[] systemServerCpuTimes) {
+ mSystemServerCpuTimes = systemServerCpuTimes;
+ }
+
+ @Override
+ public boolean perClusterTimesAvailable() {
+ return true;
+ }
+
+ @Override
+ public void readDelta(@Nullable Callback<long[]> cb) {
+ if (cb != null) {
+ cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
+ }
+ }
+ }
+
+ private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader {
+ private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
+
+ MockServerCpuThreadReader() {
+ super(null);
+ }
+
+ public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
+ mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
+ mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
+ }
+
+ @Override
+ public SystemServiceCpuThreadTimes readDelta() {
+ return mThreadTimes;
+ }
+ }
+
+ private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider {
+ @Nullable
+ @Override
+ protected int[] getUserIds() {
+ return new int[0];
+ }
+
+ @Override
+ public boolean exists(int userId) {
+ return true;
+ }
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f834ce7..b3c8263 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -432,18 +432,6 @@
<permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<!-- Permissions required for CTS test - AdbManagerTest -->
<permission name="android.permission.MANAGE_DEBUGGING" />
- <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
- <permission name="android.car.permission.CAR_DRIVING_STATE" />
- <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
- <!-- Permissions required for ATS tests - AtsDeviceInfo -->
- <permission name="android.car.permission.CAR_DIAGNOSTICS" />
- <!-- Permissions required for ATS tests - AtsDeviceInfo -->
- <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
- <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
- <permission name="android.car.permission.CONTROL_APP_BLOCKING" />
- <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
- <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
index 3ffb8ea..a2ee065 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
@@ -23,12 +23,15 @@
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import java.util.List;
@@ -44,11 +47,20 @@
name = "AndroidFrameworkUid",
summary = "Verifies that PID, UID and user ID arguments aren't crossed",
severity = WARNING)
-public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher {
+public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher,
+ NewClassTreeMatcher {
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
- final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
- final List<? extends ExpressionTree> args = tree.getArguments();
+ return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree);
+ }
+
+ @Override
+ public Description matchNewClass(NewClassTree tree, VisitorState state) {
+ return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree);
+ }
+
+ private Description matchArguments(List<VarSymbol> vars,
+ List<? extends ExpressionTree> args, Tree tree) {
for (int i = 0; i < Math.min(vars.size(), args.size()); i++) {
final Flavor varFlavor = getFlavor(vars.get(i));
final Flavor argFlavor = getFlavor(args.get(i));
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
index 74da947..75341fd 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
@@ -34,7 +34,7 @@
}
@Test
- public void testTypical() {
+ public void testTypical_methodInvocation() {
compilationHelper
.addSourceLines("Example.java",
"public abstract class Example {",
@@ -57,7 +57,30 @@
}
@Test
- public void testCallingUid() {
+ public void testTypical_newClass() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "public abstract class Example {",
+ " class Bar { Bar(int pid, int uid, int userId) {} }",
+ " abstract int getUserId();",
+ " void foo(int pid, int uid, int userId, int unrelated) {",
+ " new Bar(0, 0, 0);",
+ " new Bar(pid, uid, userId);",
+ " new Bar(pid, uid, getUserId());",
+ " new Bar(unrelated, unrelated, unrelated);",
+ " // BUG: Diagnostic contains:",
+ " new Bar(uid, pid, userId);",
+ " // BUG: Diagnostic contains:",
+ " new Bar(pid, userId, uid);",
+ " // BUG: Diagnostic contains:",
+ " new Bar(getUserId(), 0, 0);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testCallingUid_methodInvocation() {
compilationHelper
.addSourceFile("/android/os/Binder.java")
.addSourceFile("/android/os/UserHandle.java")
@@ -98,4 +121,48 @@
"}")
.doTest();
}
+
+
+ @Test
+ public void testCallingUid_newClass() {
+ compilationHelper
+ .addSourceFile("/android/os/Binder.java")
+ .addSourceFile("/android/os/UserHandle.java")
+ .addSourceLines("Example.java",
+ "import android.os.Binder;",
+ "import android.os.UserHandle;",
+ "public abstract class Example {",
+ " int callingUserId;",
+ " int callingUid;",
+ " class Foo { Foo(int callingUserId) {} }",
+ " class Bar { Bar(int callingUid) {} }",
+ " void doUserId(int callingUserId) {",
+ " new Foo(UserHandle.getUserId(Binder.getCallingUid()));",
+ " new Foo(this.callingUserId);",
+ " new Foo(callingUserId);",
+ " // BUG: Diagnostic contains:",
+ " new Foo(Binder.getCallingUid());",
+ " // BUG: Diagnostic contains:",
+ " new Foo(this.callingUid);",
+ " // BUG: Diagnostic contains:",
+ " new Foo(callingUid);",
+ " }",
+ " void doUid(int callingUserId) {",
+ " new Bar(Binder.getCallingUid());",
+ " new Bar(this.callingUid);",
+ " new Bar(callingUid);",
+ " // BUG: Diagnostic contains:",
+ " new Bar(UserHandle.getUserId(Binder.getCallingUid()));",
+ " // BUG: Diagnostic contains:",
+ " new Bar(this.callingUserId);",
+ " // BUG: Diagnostic contains:",
+ " new Bar(callingUserId);",
+ " }",
+ " void doInner() {",
+ " // BUG: Diagnostic contains:",
+ " new Foo(UserHandle.getUserId(callingUserId));",
+ " }",
+ "}")
+ .doTest();
+ }
}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 54618a5..70ef69d 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11900,6 +11900,7 @@
field public static final int INVALID_ID = -1; // 0xffffffff
field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+ field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
index 5dcb9de..a2cd044 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
@@ -39,7 +39,7 @@
/**
* A class that detects unsafe apps.
- * An app is considered safe if is a system app or installed through whitelisted sources.
+ * An app is considered safe if is a system app or installed through allowed sources.
*/
@Singleton
public class SideLoadedAppDetector {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index 31f1170..f77294e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -19,6 +19,8 @@
import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE;
import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -40,11 +42,13 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
@CarSystemUiTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
+// TODO(b/162866441): Refactor to use the Executor pattern instead.
public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -52,13 +56,15 @@
private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
private TestableLooper mTestableLooper;
+ private Handler mHandler;
private Handler mTestHandler;
private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() throws Exception {
mTestableLooper = TestableLooper.get(this);
- mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+ mHandler = new Handler(mTestableLooper.getLooper());
+ mTestHandler = spy(mHandler);
mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
BLUETOOTH_REMOTE_ADDRESS);
mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
@@ -74,8 +80,14 @@
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mTestableLooper.processAllMessages();
+ waitForIdleSync();
- verify(mTestHandler).post(any());
+ mHandler.post(() -> {
+ ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mTestHandler).post(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).isNotNull();
+ assertThat(argumentCaptor.getValue()).isNotEqualTo(this);
+ });
}
@Test
@@ -86,8 +98,11 @@
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mTestableLooper.processAllMessages();
+ waitForIdleSync();
- verify(mTestHandler, never()).post(any());
+ mHandler.post(() -> {
+ verify(mTestHandler, never()).post(any());
+ });
}
@Test
@@ -97,8 +112,11 @@
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mTestableLooper.processAllMessages();
+ waitForIdleSync();
- verify(mTestHandler, never()).post(any());
+ mHandler.post(() -> {
+ verify(mTestHandler, never()).post(any());
+ });
}
@Test
@@ -108,7 +126,10 @@
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mTestableLooper.processAllMessages();
+ waitForIdleSync();
- verify(mTestHandler, never()).post(any());
+ mHandler.post(() -> {
+ verify(mTestHandler, never()).post(any());
+ });
}
}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
similarity index 87%
rename from packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
index a12aa83..a08f566 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
@@ -19,9 +19,9 @@
import android.os.Bundle;
/**
- * A controller that manages event for master switch.
+ * A controller that manages event for Primary switch.
*/
-public abstract class MasterSwitchController extends SwitchController {
+public abstract class PrimarySwitchController extends SwitchController {
@Override
protected final MetaData getMetaData() {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
index 73f1a90..f2b3e30 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
@@ -88,7 +88,7 @@
controller.setAuthority(mAuthority);
mControllerMap.put(key, controller);
- if (!(controller instanceof MasterSwitchController)) {
+ if (!(controller instanceof PrimarySwitchController)) {
mSwitchDataList.add(controller.getBundle());
}
});
@@ -116,7 +116,7 @@
switch (method) {
case METHOD_GET_SWITCH_DATA:
- if (!(controller instanceof MasterSwitchController)) {
+ if (!(controller instanceof PrimarySwitchController)) {
return controller.getBundle();
}
break;
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
index f757aa4..b29595e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -24,7 +24,7 @@
import com.android.settingslib.core.AbstractPreferenceController;
/**
- * This controller is used handle changes for the master switch in the developer options page.
+ * This controller is used handle changes for the primary switch in the developer options page.
*
* All Preference Controllers that are a part of the developer options page should inherit this
* class.
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
similarity index 69%
rename from packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
rename to packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 3c647a7..c501b3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -34,44 +34,50 @@
import com.android.internal.util.ArrayUtils;
/**
- * Handles getting/changing the whitelist for the exceptions to battery saving features.
+ * Handles getting/changing the allowlist for the exceptions to battery saving features.
*/
-public class PowerWhitelistBackend {
+public class PowerAllowlistBackend {
- private static final String TAG = "PowerWhitelistBackend";
+ private static final String TAG = "PowerAllowlistBackend";
private static final String DEVICE_IDLE_SERVICE = "deviceidle";
- private static PowerWhitelistBackend sInstance;
+ private static PowerAllowlistBackend sInstance;
private final Context mAppContext;
private final IDeviceIdleController mDeviceIdleService;
- private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
- private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();
+ private final ArraySet<String> mAllowlistedApps = new ArraySet<>();
+ private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>();
private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
- public PowerWhitelistBackend(Context context) {
+ public PowerAllowlistBackend(Context context) {
this(context, IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(DEVICE_IDLE_SERVICE)));
}
@VisibleForTesting
- PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService) {
+ PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService) {
mAppContext = context.getApplicationContext();
mDeviceIdleService = deviceIdleService;
refreshList();
}
- public int getWhitelistSize() {
- return mWhitelistedApps.size();
+ public int getAllowlistSize() {
+ return mAllowlistedApps.size();
}
- public boolean isSysWhitelisted(String pkg) {
- return mSysWhitelistedApps.contains(pkg);
+ /**
+ * Check if target package is in System allow list
+ */
+ public boolean isSysAllowlisted(String pkg) {
+ return mSysAllowlistedApps.contains(pkg);
}
- public boolean isWhitelisted(String pkg) {
- if (mWhitelistedApps.contains(pkg)) {
+ /**
+ * Check if target package is in allow list
+ */
+ public boolean isAllowlisted(String pkg) {
+ if (mAllowlistedApps.contains(pkg)) {
return true;
}
@@ -87,7 +93,7 @@
*/
public boolean isDefaultActiveApp(String pkg) {
// Additionally, check if pkg is default dialer/sms. They are considered essential apps and
- // should be automatically whitelisted (otherwise user may be able to set restriction on
+ // should be automatically allowlisted (otherwise user may be able to set restriction on
// them, leading to bad device behavior.)
if (mDefaultActiveApps.contains(pkg)) {
@@ -103,12 +109,17 @@
return false;
}
- public boolean isWhitelisted(String[] pkgs) {
+ /**
+ *
+ * @param pkgs a list of packageName
+ * @return true when one of package is in allow list
+ */
+ public boolean isAllowlisted(String[] pkgs) {
if (ArrayUtils.isEmpty(pkgs)) {
return false;
}
for (String pkg : pkgs) {
- if (isWhitelisted(pkg)) {
+ if (isAllowlisted(pkg)) {
return true;
}
}
@@ -116,40 +127,51 @@
return false;
}
+ /**
+ * Add app into power save allow list.
+ * @param pkg packageName
+ */
public void addApp(String pkg) {
try {
mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
- mWhitelistedApps.add(pkg);
+ mAllowlistedApps.add(pkg);
} catch (RemoteException e) {
Log.w(TAG, "Unable to reach IDeviceIdleController", e);
}
}
+ /**
+ * Remove package from power save allow list.
+ * @param pkg
+ */
public void removeApp(String pkg) {
try {
mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
- mWhitelistedApps.remove(pkg);
+ mAllowlistedApps.remove(pkg);
} catch (RemoteException e) {
Log.w(TAG, "Unable to reach IDeviceIdleController", e);
}
}
+ /**
+ * Refresh all of lists
+ */
@VisibleForTesting
public void refreshList() {
- mSysWhitelistedApps.clear();
- mWhitelistedApps.clear();
+ mSysAllowlistedApps.clear();
+ mAllowlistedApps.clear();
mDefaultActiveApps.clear();
if (mDeviceIdleService == null) {
return;
}
try {
- final String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist();
- for (String app : whitelistedApps) {
- mWhitelistedApps.add(app);
+ final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
+ for (String app : allowlistedApps) {
+ mAllowlistedApps.add(app);
}
- final String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist();
- for (String app : sysWhitelistedApps) {
- mSysWhitelistedApps.add(app);
+ final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
+ for (String app : sysAllowlistedApps) {
+ mSysAllowlistedApps.add(app);
}
final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY);
@@ -171,9 +193,13 @@
}
}
- public static PowerWhitelistBackend getInstance(Context context) {
+ /**
+ * @param context
+ * @return a PowerAllowlistBackend object
+ */
+ public static PowerAllowlistBackend getInstance(Context context) {
if (sInstance == null) {
- sInstance = new PowerWhitelistBackend(context);
+ sInstance = new PowerAllowlistBackend(context);
}
return sInstance;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
index 4941f7e..8ac4349 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -135,7 +135,7 @@
// Ignore
}
} else {
- // Blacklist all other apps, system or downloaded
+ // Denylist all other apps, system or downloaded
try {
ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
if (info != null) {
@@ -258,11 +258,11 @@
}
}
- // Establish master/slave relationship for entries that share a package name
+ // Establish primary/secondary relationship for entries that share a package name
HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
for (SelectableAppInfo info : mVisibleApps) {
if (packageMap.containsKey(info.packageName)) {
- info.masterEntry = packageMap.get(info.packageName);
+ info.primaryEntry = packageMap.get(info.packageName);
} else {
packageMap.put(info.packageName, info);
}
@@ -366,12 +366,12 @@
public CharSequence appName;
public CharSequence activityName;
public Drawable icon;
- public SelectableAppInfo masterEntry;
+ public SelectableAppInfo primaryEntry;
@Override
public String toString() {
return packageName + ": appName=" + appName + "; activityName=" + activityName
- + "; icon=" + icon + "; masterEntry=" + masterEntry;
+ + "; icon=" + icon + "; primaryEntry=" + primaryEntry;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 11c799e..94e28f2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -290,12 +290,12 @@
@Test
public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() {
EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
- enforcedAdmin.enforcedRestriction = "Dummy";
+ enforcedAdmin.enforcedRestriction = "Fake";
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
- assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy");
+ assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Fake");
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
similarity index 86%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
index 69d0f2e..9e4cde8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
@@ -23,16 +23,16 @@
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
-public class MasterSwitchControllerTest {
+public class PrimarySwitchControllerTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
- private MasterSwitchController mController;
+ private PrimarySwitchController mController;
@Before
public void setUp() {
- mController = new TestMasterSwitchController("123");
+ mController = new TestPrimarySwitchController("123");
}
@Test
@@ -49,11 +49,11 @@
mController.getBundle();
}
- static class TestMasterSwitchController extends MasterSwitchController {
+ static class TestPrimarySwitchController extends PrimarySwitchController {
private String mKey;
- TestMasterSwitchController(String key) {
+ TestPrimarySwitchController(String key) {
mKey = key;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
index a740e68..bd0100b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
@@ -35,7 +35,7 @@
import android.content.pm.ProviderInfo;
import android.os.Bundle;
-import com.android.settingslib.drawer.MasterSwitchControllerTest.TestMasterSwitchController;
+import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController;
import com.android.settingslib.drawer.SwitchController.MetaData;
import org.junit.Before;
@@ -124,8 +124,8 @@
}
@Test
- public void getSwitchData_shouldNotReturnMasterSwitchData() {
- final SwitchController controller = new TestMasterSwitchController("123");
+ public void getSwitchData_shouldNotReturnPrimarySwitchData() {
+ final SwitchController controller = new TestPrimarySwitchController("123");
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
similarity index 67%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 2090892..4f11fb1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -47,7 +47,7 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
-public class PowerWhitelistBackendTest {
+public class PowerAllowlistBackendTest {
private static final String PACKAGE_ONE = "com.example.packageone";
private static final String PACKAGE_TWO = "com.example.packagetwo";
@@ -56,7 +56,7 @@
private IDeviceIdleController mDeviceIdleService;
@Mock
private DevicePolicyManager mDevicePolicyManager;
- private PowerWhitelistBackend mPowerWhitelistBackend;
+ private PowerAllowlistBackend mPowerAllowlistBackend;
private ShadowPackageManager mPackageManager;
private Context mContext;
@@ -74,81 +74,81 @@
mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
- mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService);
+ mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
}
@Test
- public void testIsWhitelisted() throws Exception {
+ public void testIsAllowlisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
- mPowerWhitelistBackend.refreshList();
+ mPowerAllowlistBackend.refreshList();
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
- assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue();
- assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
- mPowerWhitelistBackend.addApp(PACKAGE_TWO);
+ mPowerAllowlistBackend.addApp(PACKAGE_TWO);
verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO);
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue();
- assertThat(mPowerWhitelistBackend.isWhitelisted(
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(
new String[] {PACKAGE_ONE, PACKAGE_TWO})).isTrue();
- mPowerWhitelistBackend.removeApp(PACKAGE_TWO);
+ mPowerAllowlistBackend.removeApp(PACKAGE_TWO);
verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO);
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
- assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue();
- assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
- mPowerWhitelistBackend.removeApp(PACKAGE_ONE);
+ mPowerAllowlistBackend.removeApp(PACKAGE_ONE);
verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE);
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
- assertThat(mPowerWhitelistBackend.isWhitelisted(
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(
new String[] {PACKAGE_ONE, PACKAGE_TWO})).isFalse();
}
@Test
- public void isWhitelisted_shouldWhitelistDefaultSms() {
+ public void isAllowlisted_shouldAllowlistDefaultSms() {
final String testSms = "com.android.test.defaultsms";
ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver"));
- mPowerWhitelistBackend.refreshList();
+ mPowerAllowlistBackend.refreshList();
- assertThat(mPowerWhitelistBackend.isWhitelisted(testSms)).isTrue();
- assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testSms)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(testSms)).isTrue();
+ assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms)).isTrue();
}
@Test
- public void isWhitelisted_shouldWhitelistDefaultDialer() {
+ public void isAllowlisted_shouldAllowlistDefaultDialer() {
final String testDialer = "com.android.test.defaultdialer";
ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer);
- mPowerWhitelistBackend.refreshList();
+ mPowerAllowlistBackend.refreshList();
- assertThat(mPowerWhitelistBackend.isWhitelisted(testDialer)).isTrue();
- assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testDialer)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer)).isTrue();
+ assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer)).isTrue();
}
@Test
- public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() {
+ public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() {
doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
- assertThat(mPowerWhitelistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
}
@Test
- public void testIsSystemWhitelisted() throws Exception {
+ public void testIsSystemAllowlisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
- mPowerWhitelistBackend.refreshList();
+ mPowerAllowlistBackend.refreshList();
- assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_ONE)).isTrue();
- assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse();
- assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
+ assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
+ assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index b930aa6..84d722a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -197,61 +197,61 @@
public void isValidSystemNonAuxAsciiCapableIme() {
// System IME w/ no subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(true, false)))
+ createFakeIme(true, false)))
.isFalse();
// System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+ createFakeIme(true, false, createFakeSubtype("keyboard", false, false))))
.isFalse();
// System IME w/ non-Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+ createFakeIme(true, false, createFakeSubtype("keyboard", false, true))))
.isTrue();
// System IME w/ Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+ createFakeIme(true, true, createFakeSubtype("keyboard", true, true))))
.isFalse();
// System IME w/ non-Aux and ASCII-capable "voice" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(true, false, createDummySubtype("voice", false, true))))
+ createFakeIme(true, false, createFakeSubtype("voice", false, true))))
.isFalse();
// System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(true, false,
- createDummySubtype("keyboard", false, true),
- createDummySubtype("keyboard", false, false))))
+ createFakeIme(true, false,
+ createFakeSubtype("keyboard", false, true),
+ createFakeSubtype("keyboard", false, false))))
.isTrue();
// Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
- createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+ createFakeIme(false, false, createFakeSubtype("keyboard", false, true))))
.isFalse();
}
- private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+ private static InputMethodInfo createFakeIme(boolean isSystem, boolean isAuxIme,
InputMethodSubtype... subtypes) {
final ResolveInfo ri = new ResolveInfo();
final ServiceInfo si = new ServiceInfo();
final ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = "com.example.android.dummyime";
+ ai.packageName = "com.example.android.fakeime";
ai.enabled = true;
ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
si.applicationInfo = ai;
si.enabled = true;
- si.packageName = "com.example.android.dummyime";
- si.name = "Dummy IME";
+ si.packageName = "com.example.android.fakeime";
+ si.name = "Fake IME";
si.exported = true;
- si.nonLocalizedLabel = "Dummy IME";
+ si.nonLocalizedLabel = "Fake IME";
ri.serviceInfo = si;
return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
}
- private static InputMethodSubtype createDummySubtype(
+ private static InputMethodSubtype createFakeSubtype(
String mode, boolean isAuxiliary, boolean isAsciiCapable) {
return new InputMethodSubtypeBuilder()
.setSubtypeNameResId(0)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 5171dda..97d8705 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -195,55 +195,55 @@
public void isValidNonAuxAsciiCapableIme() {
// IME w/ no subtype
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
- createDummyIme(false)))
+ createFakeIme(false)))
.isFalse();
// IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
- createDummyIme(false, createDummySubtype("keyboard", false, false))))
+ createFakeIme(false, createFakeSubtype("keyboard", false, false))))
.isFalse();
// IME w/ non-Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
- createDummyIme(false, createDummySubtype("keyboard", false, true))))
+ createFakeIme(false, createFakeSubtype("keyboard", false, true))))
.isTrue();
// IME w/ Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
- createDummyIme(true, createDummySubtype("keyboard", true, true))))
+ createFakeIme(true, createFakeSubtype("keyboard", true, true))))
.isFalse();
// IME w/ non-Aux and ASCII-capable "voice" subtype
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
- createDummyIme(false, createDummySubtype("voice", false, true))))
+ createFakeIme(false, createFakeSubtype("voice", false, true))))
.isFalse();
// IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
- createDummyIme(false,
- createDummySubtype("keyboard", false, true),
- createDummySubtype("keyboard", false, false))))
+ createFakeIme(false,
+ createFakeSubtype("keyboard", false, true),
+ createFakeSubtype("keyboard", false, false))))
.isTrue();
}
- private static InputMethodInfo createDummyIme(boolean isAuxIme,
+ private static InputMethodInfo createFakeIme(boolean isAuxIme,
InputMethodSubtype... subtypes) {
final ResolveInfo ri = new ResolveInfo();
final ServiceInfo si = new ServiceInfo();
final ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = "com.example.android.dummyime";
+ ai.packageName = "com.example.android.fakeime";
ai.enabled = true;
si.applicationInfo = ai;
si.enabled = true;
- si.packageName = "com.example.android.dummyime";
- si.name = "Dummy IME";
+ si.packageName = "com.example.android.fakeime";
+ si.name = "Fake IME";
si.exported = true;
- si.nonLocalizedLabel = "Dummy IME";
+ si.nonLocalizedLabel = "Fake IME";
ri.serviceInfo = si;
return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
}
- private static InputMethodSubtype createDummySubtype(
+ private static InputMethodSubtype createFakeSubtype(
String mode, boolean isAuxiliary, boolean isAsciiCapable) {
return new InputMethodSubtypeBuilder()
.setSubtypeNameResId(0)
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index aa96087..319b44c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,19 +320,6 @@
<!-- Permissions required for CTS test - AdbManagerTest -->
<uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
- <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
- <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
- <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
- <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
- <!-- Permissions required for ATS tests - AtsDeviceInfo -->
- <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" />
- <!-- Permissions required for ATS tests - AtsDeviceInfo -->
- <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
- <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
- <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" />
- <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
- <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
-
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 176035f..1c680bb 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -7,7 +7,8 @@
static_libs: [
"androidx.legacy_legacy-support-v4",
- "setup-wizard-lib",
+ "setupcompat",
+ "setupdesign",
],
resource_dirs: ["res"],
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
index 873f6c5..e7368f3 100644
--- a/packages/SimAppDialog/AndroidManifest.xml
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -23,7 +23,7 @@
android:name=".InstallCarrierAppActivity"
android:exported="true"
android:permission="android.permission.NETWORK_SETTINGS"
- android:theme="@style/SuwThemeGlif.Light">
+ android:theme="@style/SudThemeGlif.Light">
</activity>
</application>
</manifest>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 12f9bb6..68113db 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -14,18 +14,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.setupwizardlib.GlifLayout
+<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/ic_signal_cellular_alt_rounded"
- app:suwHeaderText="@string/install_carrier_app_title"
- app:suwFooter="@layout/install_carrier_app_footer">
+ app:sucHeaderText="@string/install_carrier_app_title">
<LinearLayout
- style="@style/SuwContentFrame"
+ style="@style/SudContentFrame"
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -33,12 +32,12 @@
<TextView
android:id="@+id/install_carrier_app_description"
- style="@style/SuwDescription.Glif"
+ style="@style/SudDescription.Glif"
android:text="@string/install_carrier_app_description_default"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
- <com.android.setupwizardlib.view.FillContentLayout
+ <com.google.android.setupdesign.view.FillContentLayout
android:id="@+id/illo_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -47,12 +46,12 @@
<ImageView
android:src="@drawable/illo_sim_app_dialog"
- style="@style/SuwContentIllustration"
+ style="@style/SudContentIllustration"
android:contentDescription="@string/install_carrier_app_image_content_description"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- </com.android.setupwizardlib.view.FillContentLayout>
-</LinearLayout>
+ </com.google.android.setupdesign.view.FillContentLayout>
+ </LinearLayout>
-</com.android.setupwizardlib.GlifLayout>
+</com.google.android.setupdesign.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
deleted file mode 100644
index 10dcb77..0000000
--- a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 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.
--->
-
-<com.android.setupwizardlib.view.ButtonBarLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/footer"
- style="@style/SuwGlifButtonBar.Stackable"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <Button
- android:id="@+id/skip_button"
- style="@style/SuwGlifButton.Secondary"
- android:text="@string/install_carrier_app_defer_action"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
-
- <Button
- android:id="@+id/download_button"
- style="@style/SuwGlifButton.Primary"
- android:text="@string/install_carrier_app_download_action"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml
new file mode 100644
index 0000000..824e380
--- /dev/null
+++ b/packages/SimAppDialog/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+
+ <style name="SetupWizardPartnerResource">
+ <!-- Disable to use partner overlay theme for outside setupwizard flow. -->
+ <item name="sucUsePartnerResource">false</item>
+ <!-- Enable heavy theme style inside setupwizard flow. -->
+ <item name="sudUsePartnerHeavyTheme">true</item>
+ </style>
+
+</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index abe82a8..0b6f9bb 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -17,14 +17,17 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.Bundle;
import android.sysprop.SetupWizardProperties;
import android.text.TextUtils;
import android.view.View;
-import android.widget.Button;
import android.widget.TextView;
-import com.android.setupwizardlib.util.WizardManagerHelper;
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.util.ThemeResolver;
/**
* Activity that gives a user the choice to download the SIM app or defer until a later time
@@ -35,7 +38,7 @@
* Can display the carrier app name if its passed into the intent with key
* {@link #BUNDLE_KEY_CARRIER_NAME}
*/
-public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
+public class InstallCarrierAppActivity extends Activity {
/**
* Key for the carrier app name that will be displayed as the app to download. If unset, a
* default description will be used
@@ -50,20 +53,33 @@
protected void onCreate(Bundle icicle) {
// Setup theme for aosp/pixel
setTheme(
- WizardManagerHelper.getThemeRes(
- SetupWizardProperties.theme().orElse(""),
- R.style.SuwThemeGlif_Light
- )
- );
+ new ThemeResolver.Builder()
+ .setDefaultTheme(R.style.SudThemeGlifV3_Light)
+ .build()
+ .resolve(SetupWizardProperties.theme().orElse(""),
+ /* suppressDayNight= */ false));
super.onCreate(icicle);
setContentView(R.layout.install_carrier_app_activity);
- Button notNowButton = findViewById(R.id.skip_button);
- notNowButton.setOnClickListener(this);
+ GlifLayout layout = findViewById(R.id.setup_wizard_layout);
+ FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
+ mixin.setSecondaryButton(
+ new FooterButton.Builder(this)
+ .setText(R.string.install_carrier_app_defer_action)
+ .setListener(this::onSkipButtonClick)
+ .setButtonType(FooterButton.ButtonType.SKIP)
+ .setTheme(R.style.SudGlifButton_Secondary)
+ .build());
- Button downloadButton = findViewById(R.id.download_button);
- downloadButton.setOnClickListener(this);
+ mixin.setPrimaryButton(
+ new FooterButton.Builder(this)
+ .setText(R.string.install_carrier_app_download_action)
+ .setListener(this::onDownloadButtonClick)
+ .setButtonType(FooterButton.ButtonType.OTHER)
+ .setTheme(R.style.SudGlifButton_Primary)
+ .build());
+
// Show/hide illo depending on whether one was provided in a resource overlay
boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
@@ -82,15 +98,17 @@
}
@Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.skip_button:
- finish(DEFER_RESULT);
- break;
- case R.id.download_button:
- finish(DOWNLOAD_RESULT);
- break;
- }
+ protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+ theme.applyStyle(R.style.SetupWizardPartnerResource, true);
+ super.onApplyThemeResource(theme, resid, first);
+ }
+
+ protected void onSkipButtonClick(View view) {
+ finish(DEFER_RESULT);
+ }
+
+ protected void onDownloadButtonClick(View view) {
+ finish(DOWNLOAD_RESULT);
}
private void finish(int resultCode) {
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fa620df..fba43a6 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -515,13 +515,11 @@
<!-- Whether or not to add a "people" notifications section -->
<bool name="config_usePeopleFiltering">false</bool>
- <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that
- are part of the blacklist are never displayed. Each item in the blacklist must be a string
- defined in core/res/res/config.xml to properly blacklist the icon.
-
- TODO: See if we can rename this config variable.
+ <!-- Defines system icons to be excluded from the display. That is to say, the icons in the
+ status bar that are part of this list are never displayed. Each item in the list must be a
+ string defined in core/res/res/config.xml to properly exclude the icon.
-->
- <string-array name="config_statusBarIconBlackList" translatable="false">
+ <string-array name="config_statusBarIconsToExclude" translatable="false">
<item>@*android:string/status_bar_rotate</item>
<item>@*android:string/status_bar_headset</item>
</string-array>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 816bcf8..d5f74a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -53,8 +53,8 @@
ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION;
@VisibleForTesting
- protected WindowMagnificationController mWindowMagnificationController;
- protected final ModeSwitchesController mModeSwitchesController;
+ protected WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+ private final ModeSwitchesController mModeSwitchesController;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
@@ -72,6 +72,11 @@
Context.ACCESSIBILITY_SERVICE);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
+ final WindowMagnificationController controller = new WindowMagnificationController(mContext,
+ mHandler, new SfVsyncFrameCallbackProvider(), null,
+ new SurfaceControl.Transaction(), this);
+ mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+ mContext, controller);
}
@Override
@@ -81,9 +86,7 @@
return;
}
mLastConfiguration.setTo(newConfig);
- if (mWindowMagnificationController != null) {
- mWindowMagnificationController.onConfigurationChanged(configDiff);
- }
+ mWindowMagnificationAnimationController.onConfigurationChanged(configDiff);
if (mModeSwitchesController != null) {
mModeSwitchesController.onConfigurationChanged(configDiff);
}
@@ -97,39 +100,25 @@
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
//TODO: b/144080869 support multi-display.
- if (mWindowMagnificationController == null) {
- mWindowMagnificationController = new WindowMagnificationController(mContext,
- mHandler,
- new SfVsyncFrameCallbackProvider(),
- null, new SurfaceControl.Transaction(),
- this);
- }
- mWindowMagnificationController.enableWindowMagnification(scale, centerX, centerY);
+ mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY);
}
@MainThread
void setScale(int displayId, float scale) {
//TODO: b/144080869 support multi-display.
- if (mWindowMagnificationController != null) {
- mWindowMagnificationController.setScale(scale);
- }
+ mWindowMagnificationAnimationController.setScale(scale);
}
@MainThread
void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
//TODO: b/144080869 support multi-display.
- if (mWindowMagnificationController != null) {
- mWindowMagnificationController.moveWindowMagnifier(offsetX, offsetY);
- }
+ mWindowMagnificationAnimationController.moveWindowMagnifier(offsetX, offsetY);
}
@MainThread
void disableWindowMagnification(int displayId) {
//TODO: b/144080869 support multi-display.
- if (mWindowMagnificationController != null) {
- mWindowMagnificationController.deleteWindowMagnification();
- }
- mWindowMagnificationController = null;
+ mWindowMagnificationAnimationController.deleteWindowMagnification();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
new file mode 100644
index 0000000..ae51623
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -0,0 +1,272 @@
+/*
+ * 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.systemui.accessibility;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.animation.AccelerateInterpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides same functionality of {@link WindowMagnificationController}. Some methods run with
+ * the animation.
+ */
+class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUpdateListener,
+ Animator.AnimatorListener {
+
+ private static final String TAG = "WindowMagnificationBridge";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATE_DISABLED, STATE_ENABLED, STATE_DISABLING, STATE_ENABLING})
+ @interface MagnificationState {}
+
+ //The window magnification is disabled.
+ private static final int STATE_DISABLED = 0;
+ //The window magnification is enabled.
+ private static final int STATE_ENABLED = 1;
+ //The window magnification is going to be disabled when the animation is end.
+ private static final int STATE_DISABLING = 2;
+ //The animation is running for enabling the window magnification.
+ private static final int STATE_ENABLING = 3;
+
+ private final WindowMagnificationController mController;
+ private final ValueAnimator mValueAnimator;
+ private final AnimationSpec mStartSpec = new AnimationSpec();
+ private final AnimationSpec mEndSpec = new AnimationSpec();
+ private final Context mContext;
+
+ @MagnificationState
+ private int mState = STATE_DISABLED;
+
+ WindowMagnificationAnimationController(
+ Context context, WindowMagnificationController controller) {
+ this(context, controller, newValueAnimator(context.getResources()));
+ }
+
+ @VisibleForTesting
+ WindowMagnificationAnimationController(Context context,
+ WindowMagnificationController controller, ValueAnimator valueAnimator) {
+ mContext = context;
+ mController = controller;
+ mValueAnimator = valueAnimator;
+ mValueAnimator.addUpdateListener(this);
+ mValueAnimator.addListener(this);
+ }
+
+ /**
+ * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
+ * with transition animation. If the window magnification is not enabled, the scale will start
+ * from 1.0 and the center won't be changed during the animation. If {@link #mState} is
+ * {@code STATE_DISABLING}, the animation runs in reverse.
+ *
+ * @param scale the target scale, or {@link Float#NaN} to leave unchanged.
+ * @param centerX the screen-relative X coordinate around which to center,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param centerY the screen-relative Y coordinate around which to center,
+ * or {@link Float#NaN} to leave unchanged.
+ *
+ * @see #onAnimationUpdate(ValueAnimator)
+ */
+ void enableWindowMagnification(float scale, float centerX, float centerY) {
+ if (mState == STATE_ENABLING) {
+ mValueAnimator.cancel();
+ }
+ setupEnableAnimationSpecs(scale, centerX, centerY);
+
+ if (mEndSpec.equals(mStartSpec)) {
+ setState(STATE_ENABLED);
+ } else {
+ if (mState == STATE_DISABLING) {
+ mValueAnimator.reverse();
+ } else {
+ mValueAnimator.start();
+ }
+ setState(STATE_ENABLING);
+ }
+ }
+
+ private void setupEnableAnimationSpecs(float scale, float centerX, float centerY) {
+ final float currentScale = mController.getScale();
+ final float currentCenterX = mController.getCenterX();
+ final float currentCenterY = mController.getCenterY();
+
+ if (mState == STATE_DISABLED) {
+ //We don't need to offset the center during the animation.
+ mStartSpec.set(/* scale*/ 1.0f, centerX, centerY);
+ mEndSpec.set(Float.isNaN(scale) ? mContext.getResources().getInteger(
+ R.integer.magnification_default_scale) : scale, centerX, centerY);
+ } else {
+ mStartSpec.set(currentScale, currentCenterX, currentCenterY);
+ mEndSpec.set(Float.isNaN(scale) ? currentScale : scale,
+ Float.isNaN(centerX) ? currentCenterX : centerX,
+ Float.isNaN(centerY) ? currentCenterY : centerY);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "SetupEnableAnimationSpecs : mStartSpec = " + mStartSpec + ", endSpec = "
+ + mEndSpec);
+ }
+ }
+
+ /**
+ * Wraps {@link WindowMagnificationController#setScale(float)}. If the animation is
+ * running, it has no effect.
+ */
+ void setScale(float scale) {
+ if (mValueAnimator.isRunning()) {
+ return;
+ }
+ mController.setScale(scale);
+ }
+
+ /**
+ * Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
+ * animation. If the window magnification is enabling, it runs the animation in reverse.
+ */
+ void deleteWindowMagnification() {
+ if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
+ return;
+ }
+ mStartSpec.set(/* scale*/ 1.0f, Float.NaN, Float.NaN);
+ mEndSpec.set(/* scale*/ mController.getScale(), Float.NaN, Float.NaN);
+
+ mValueAnimator.reverse();
+ setState(STATE_DISABLING);
+ }
+
+ /**
+ * Wraps {@link WindowMagnificationController#moveWindowMagnifier(float, float)}. If the
+ * animation is running, it has no effect.
+ * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+ * current screen pixels.
+ * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+ * current screen pixels.
+ */
+ void moveWindowMagnifier(float offsetX, float offsetY) {
+ if (mValueAnimator.isRunning()) {
+ return;
+ }
+ mController.moveWindowMagnifier(offsetX, offsetY);
+ }
+
+ void onConfigurationChanged(int configDiff) {
+ mController.onConfigurationChanged(configDiff);
+ }
+
+ private void setState(@MagnificationState int state) {
+ if (DEBUG) {
+ Log.d(TAG, "setState from " + mState + " to " + state);
+ }
+ mState = state;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mState == STATE_DISABLING) {
+ mController.deleteWindowMagnification();
+ setState(STATE_DISABLED);
+ } else if (mState == STATE_ENABLING) {
+ setState(STATE_ENABLED);
+ } else {
+ Log.w(TAG, "onAnimationEnd unexpected state:" + mState);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final float fract = animation.getAnimatedFraction();
+ final float sentScale = mStartSpec.mScale + (mEndSpec.mScale - mStartSpec.mScale) * fract;
+ final float centerX =
+ mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract;
+ final float centerY =
+ mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract;
+ mController.enableWindowMagnification(sentScale, centerX, centerY);
+ }
+
+ private static ValueAnimator newValueAnimator(Resources resources) {
+ final ValueAnimator valueAnimator = new ValueAnimator();
+ valueAnimator.setDuration(
+ resources.getInteger(com.android.internal.R.integer.config_longAnimTime));
+ valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
+ valueAnimator.setFloatValues(0.0f, 1.0f);
+ return valueAnimator;
+ }
+
+ private static class AnimationSpec {
+ private float mScale = Float.NaN;
+ private float mCenterX = Float.NaN;
+ private float mCenterY = Float.NaN;
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final AnimationSpec s = (AnimationSpec) other;
+ return mScale == s.mScale && mCenterX == s.mCenterX && mCenterY == s.mCenterY;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (mScale != +0.0f ? Float.floatToIntBits(mScale) : 0);
+ result = 31 * result + (mCenterX != +0.0f ? Float.floatToIntBits(mCenterX) : 0);
+ result = 31 * result + (mCenterY != +0.0f ? Float.floatToIntBits(mCenterY) : 0);
+ return result;
+ }
+
+ void set(float scale, float centerX, float centerY) {
+ mScale = scale;
+ mCenterX = centerX;
+ mCenterY = centerY;
+ }
+
+ @Override
+ public String toString() {
+ return "AnimationSpec{"
+ + "mScale=" + mScale
+ + ", mCenterX=" + mCenterX
+ + ", mCenterY=" + mCenterY
+ + '}';
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 798b751..6d3e8ba 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -150,7 +150,7 @@
mMirrorViewGeometryVsyncCallback =
l -> {
- if (mMirrorView != null && mMirrorSurface != null) {
+ if (isWindowVisible() && mMirrorSurface != null) {
calculateSourceBounds(mMagnificationFrame, mScale);
// The final destination for the magnification surface should be at 0,0
// since the ViewRootImpl's position will change
@@ -502,7 +502,7 @@
/**
* Enables window magnification with specified parameters.
*
- * @param scale the target scale
+ * @param scale the target scale, or {@link Float#NaN} to leave unchanged
* @param centerX the screen-relative X coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
@@ -513,10 +513,10 @@
: centerX - mMagnificationFrame.exactCenterX();
final float offsetY = Float.isNaN(centerY) ? 0
: centerY - mMagnificationFrame.exactCenterY();
- mScale = scale;
+ mScale = Float.isNaN(scale) ? mScale : scale;
setMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
- if (mMirrorView == null) {
+ if (!isWindowVisible()) {
createMirrorWindow();
showControls();
} else {
@@ -527,10 +527,10 @@
/**
* Sets the scale of the magnified region if it's visible.
*
- * @param scale the target scale
+ * @param scale the target scale, or {@link Float#NaN} to leave unchanged
*/
void setScale(float scale) {
- if (mMirrorView == null || mScale == scale) {
+ if (!isWindowVisible() || mScale == scale) {
return;
}
enableWindowMagnification(scale, Float.NaN, Float.NaN);
@@ -552,4 +552,35 @@
modifyWindowMagnification(mTransaction);
}
}
+
+ /**
+ * Gets the scale.
+ * @return {@link Float#NaN} if the window is invisible.
+ */
+ float getScale() {
+ return isWindowVisible() ? mScale : Float.NaN;
+ }
+
+ /**
+ * Returns the screen-relative X coordinate of the center of the magnified bounds.
+ *
+ * @return the X coordinate. {@link Float#NaN} if the window is invisible.
+ */
+ float getCenterX() {
+ return isWindowVisible() ? mMagnificationFrame.exactCenterX() : Float.NaN;
+ }
+
+ /**
+ * Returns the screen-relative Y coordinate of the center of the magnified bounds.
+ *
+ * @return the Y coordinate. {@link Float#NaN} if the window is invisible.
+ */
+ float getCenterY() {
+ return isWindowVisible() ? mMagnificationFrame.exactCenterY() : Float.NaN;
+ }
+
+ //The window is visible when it is existed.
+ private boolean isWindowVisible() {
+ return mMirrorView != null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 4df6660..6512624 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -281,9 +281,11 @@
* @return {@code true} iff the app-op for should be shown to the user
*/
private boolean isUserVisible(int appOpCode, int uid, String packageName) {
- // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
- // which may be user senstive, so for now always show it to the user.
- if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+ // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
+ // does not correspond to a platform permission
+ // which may be user sensitive, so for now always show it to the user.
+ if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
+ || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index d5a14f7..affc5ee 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -45,7 +45,6 @@
@Singleton
class PrivacyItemController @Inject constructor(
- context: Context,
private val appOpsController: AppOpsController,
@Main uiExecutor: DelayableExecutor,
@Background private val bgExecutor: Executor,
@@ -57,16 +56,21 @@
@VisibleForTesting
internal companion object {
- val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_RECORD_AUDIO,
+ val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_RECORD_AUDIO)
+ val OPS_LOCATION = intArrayOf(
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
+ val OPS = OPS_MIC_CAMERA + OPS_LOCATION
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_USER_SWITCHED)
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
}
const val TAG = "PrivacyItemController"
+ private const val ALL_INDICATORS =
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+ private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
}
@VisibleForTesting
@@ -74,9 +78,14 @@
@Synchronized get() = field.toList() // Returns a shallow copy of the list
@Synchronized set
- private fun isPermissionsHubEnabled(): Boolean {
+ private fun isAllIndicatorsEnabled(): Boolean {
return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ ALL_INDICATORS, false)
+ }
+
+ private fun isMicCameraEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ MIC_CAMERA, false)
}
private var currentUserIds = emptyList<Int>()
@@ -94,23 +103,28 @@
uiExecutor.execute(notifyChanges)
}
- var indicatorsAvailable = isPermissionsHubEnabled()
+ var allIndicatorsAvailable = isAllIndicatorsEnabled()
private set
- @VisibleForTesting
- internal val devicePropertiesChangedListener =
+ var micCameraAvailable = isMicCameraEnabled()
+ private set
+
+ private val devicePropertiesChangedListener =
object : DeviceConfig.OnPropertiesChangedListener {
override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
- properties.getKeyset().contains(
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
- val flag = properties.getBoolean(
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
- if (indicatorsAvailable != flag) {
- // This is happening already in the UI executor, so we can iterate in the
- indicatorsAvailable = flag
- callbacks.forEach { it.get()?.onFlagChanged(flag) }
+ (properties.keyset.contains(ALL_INDICATORS) ||
+ properties.keyset.contains(MIC_CAMERA))) {
+
+ // Running on the ui executor so can iterate on callbacks
+ if (properties.keyset.contains(ALL_INDICATORS)) {
+ allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
+ callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
}
+ if (properties.keyset.contains(MIC_CAMERA)) {
+ micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
+ callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
+ }
internalUiExecutor.updateListeningState()
}
}
@@ -123,6 +137,10 @@
packageName: String,
active: Boolean
) {
+ // Check if we care about this code right now
+ if (!allIndicatorsAvailable && code in OPS_LOCATION) {
+ return
+ }
val userId = UserHandle.getUserId(uid)
if (userId in currentUserIds) {
update(false)
@@ -166,13 +184,16 @@
}
/**
- * Updates listening status based on whether there are callbacks and the indicators are enabled
+ * Updates listening status based on whether there are callbacks and the indicators are enabled.
+ *
+ * Always listen to all OPS so we don't have to figure out what we should be listening to. We
+ * still have to filter anyway. Updates are filtered in the callback.
*
* This is only called from private (add/remove)Callback and from the config listener, all in
* main thread.
*/
private fun setListeningState() {
- val listen = !callbacks.isEmpty() and indicatorsAvailable
+ val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
if (listening == listen) return
listening = listen
if (listening) {
@@ -233,14 +254,19 @@
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}
+ if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
return PrivacyItem(type, app)
}
interface Callback {
fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+
@JvmDefault
- fun onFlagChanged(flag: Boolean) {}
+ fun onFlagAllChanged(flag: Boolean) {}
+
+ @JvmDefault
+ fun onFlagMicCameraChanged(flag: Boolean) {}
}
internal inner class Receiver : BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2dc82dd..2e258d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -151,7 +151,8 @@
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
- private boolean mPermissionsHubEnabled;
+ private boolean mAllIndicatorsEnabled;
+ private boolean mMicCameraIndicatorsEnabled;
private PrivacyItemController mPrivacyItemController;
private final UiEventLogger mUiEventLogger;
@@ -178,13 +179,26 @@
}
@Override
- public void onFlagChanged(boolean flag) {
- if (mPermissionsHubEnabled != flag) {
- StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
- iconContainer.setIgnoredSlots(getIgnoredIconSlots());
- setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+ public void onFlagAllChanged(boolean flag) {
+ if (mAllIndicatorsEnabled != flag) {
+ mAllIndicatorsEnabled = flag;
+ update();
}
}
+
+ @Override
+ public void onFlagMicCameraChanged(boolean flag) {
+ if (mMicCameraIndicatorsEnabled != flag) {
+ mMicCameraIndicatorsEnabled = flag;
+ update();
+ }
+ }
+
+ private void update() {
+ StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+ iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+ }
};
@Inject
@@ -267,7 +281,8 @@
mRingerModeTextView.setSelected(true);
mNextAlarmTextView.setSelected(true);
- mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+ mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+ mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
}
public QuickQSPanel getHeaderQsPanel() {
@@ -276,13 +291,15 @@
private List<String> getIgnoredIconSlots() {
ArrayList<String> ignored = new ArrayList<>();
- ignored.add(mContext.getResources().getString(
- com.android.internal.R.string.status_bar_camera));
- ignored.add(mContext.getResources().getString(
- com.android.internal.R.string.status_bar_microphone));
- if (mPermissionsHubEnabled) {
+ if (getChipEnabled()) {
ignored.add(mContext.getResources().getString(
- com.android.internal.R.string.status_bar_location));
+ com.android.internal.R.string.status_bar_camera));
+ ignored.add(mContext.getResources().getString(
+ com.android.internal.R.string.status_bar_microphone));
+ if (mAllIndicatorsEnabled) {
+ ignored.add(mContext.getResources().getString(
+ com.android.internal.R.string.status_bar_location));
+ }
}
return ignored;
@@ -300,7 +317,7 @@
}
private void setChipVisibility(boolean chipVisible) {
- if (chipVisible && mPermissionsHubEnabled) {
+ if (chipVisible && getChipEnabled()) {
mPrivacyChip.setVisibility(View.VISIBLE);
// Makes sure that the chip is logged as viewed at most once each time QS is opened
// mListening makes sure that the callback didn't return after the user closed QS
@@ -607,7 +624,8 @@
mAlarmController.addCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
// Get the most up to date info
- mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+ mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+ mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mPrivacyItemController.addCallback(mPICCallback);
} else {
mZenController.removeCallback(this);
@@ -747,4 +765,8 @@
updateHeaderTextContainerAlphaAnimator();
}
}
+
+ private boolean getChipEnabled() {
+ return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8c485a6..d2aaaede 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -451,15 +451,17 @@
if (listening) {
if (mListeners.add(listener) && mListeners.size() == 1) {
if (DEBUG) Log.d(TAG, "handleSetListening true");
- mLifecycle.setCurrentState(RESUMED);
handleSetListening(listening);
- refreshState(); // Ensure we get at least one refresh after listening.
+ mUiHandler.post(() -> {
+ mLifecycle.setCurrentState(RESUMED);
+ refreshState(); // Ensure we get at least one refresh after listening.
+ });
}
} else {
if (mListeners.remove(listener) && mListeners.size() == 0) {
if (DEBUG) Log.d(TAG, "handleSetListening false");
- mLifecycle.setCurrentState(STARTED);
handleSetListening(listening);
+ mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED));
}
}
updateIsFullQs();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 4007abb..d40b666 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -78,6 +78,22 @@
onUndockingTask();
}
}
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId,
+ int reason) {
+ mDividerController.onActivityForcedResizable(packageName, taskId, reason);
+ }
+
+ @Override
+ public void onActivityDismissingDockedStack() {
+ mDividerController.onActivityDismissingSplitScreen();
+ }
+
+ @Override
+ public void onActivityLaunchOnSecondaryDisplayFailed() {
+ mDividerController.onActivityLaunchOnSecondaryDisplayFailed();
+ }
}
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
index 81649f6..1ee8abb 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
@@ -50,8 +50,7 @@
/**
* Controls the docked stack divider.
*/
-public class DividerController implements DividerView.DividerCallbacks,
- DisplayController.OnDisplaysChangedListener {
+public class DividerController implements DisplayController.OnDisplaysChangedListener {
static final boolean DEBUG = false;
private static final String TAG = "Divider";
@@ -257,8 +256,8 @@
mView = (DividerView)
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
- mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
- mImePositionProcessor, mWindowManagerProxy);
+ mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits,
+ mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
final int size = dctx.getResources().getDimensionPixelSize(
@@ -397,7 +396,22 @@
}
}
- /** Called when there's a task undocking. */
+ /** Called when there's an activity forced resizable. */
+ public void onActivityForcedResizable(String packageName, int taskId, int reason) {
+ mForcedResizableController.activityForcedResizable(packageName, taskId, reason);
+ }
+
+ /** Called when there's an activity dismissing split screen. */
+ public void onActivityDismissingSplitScreen() {
+ mForcedResizableController.activityDismissingSplitScreen();
+ }
+
+ /** Called when there's an activity launch on secondary display failed. */
+ public void onActivityLaunchOnSecondaryDisplayFailed() {
+ mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
+ }
+
+ /** Called when there's a task undocking. */
public void onUndockingTask() {
if (mView != null) {
mView.onUndockingTask();
@@ -426,17 +440,7 @@
mForcedResizableController.onAppTransitionFinished();
}
- @Override
- public void onDraggingStart() {
- mForcedResizableController.onDraggingStart();
- }
-
- @Override
- public void onDraggingEnd() {
- mForcedResizableController.onDraggingEnd();
- }
-
- /** Dumps current status of Divider.*/
+ /** Dumps current status of Split Screen. */
public void dump(PrintWriter pw) {
pw.print(" mVisible="); pw.println(mVisible);
pw.print(" mMinimized="); pw.println(mMinimized);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index f412cc0..ff8bab0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -28,15 +28,13 @@
import android.widget.Toast;
import com.android.systemui.R;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.function.Consumer;
/**
* Controller that decides when to show the {@link ForcedResizableInfoActivity}.
*/
-public class ForcedResizableInfoActivityController {
+final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks {
private static final String SELF_PACKAGE_NAME = "com.android.systemui";
@@ -47,12 +45,7 @@
private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
private boolean mDividerDragging;
- private final Runnable mTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- showPending();
- }
- };
+ private final Runnable mTimeoutRunnable = this::showPending;
private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
if (!exists) {
@@ -78,44 +71,28 @@
public ForcedResizableInfoActivityController(Context context,
DividerController dividerController) {
mContext = context;
- ActivityManagerWrapper.getInstance().registerTaskStackListener(
- new TaskStackChangeListener() {
- @Override
- public void onActivityForcedResizable(String packageName, int taskId,
- int reason) {
- activityForcedResizable(packageName, taskId, reason);
- }
-
- @Override
- public void onActivityDismissingDockedStack() {
- activityDismissingDockedStack();
- }
-
- @Override
- public void onActivityLaunchOnSecondaryDisplayFailed() {
- activityLaunchOnSecondaryDisplayFailed();
- }
- });
dividerController.registerInSplitScreenListener(mDockedStackExistsListener);
}
- public void onAppTransitionFinished() {
+ @Override
+ public void onDraggingStart() {
+ mDividerDragging = true;
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ }
+
+ @Override
+ public void onDraggingEnd() {
+ mDividerDragging = false;
+ showPending();
+ }
+
+ void onAppTransitionFinished() {
if (!mDividerDragging) {
showPending();
}
}
- void onDraggingStart() {
- mDividerDragging = true;
- mHandler.removeCallbacks(mTimeoutRunnable);
- }
-
- void onDraggingEnd() {
- mDividerDragging = false;
- showPending();
- }
-
- private void activityForcedResizable(String packageName, int taskId, int reason) {
+ void activityForcedResizable(String packageName, int taskId, int reason) {
if (debounce(packageName)) {
return;
}
@@ -123,12 +100,12 @@
postTimeout();
}
- private void activityDismissingDockedStack() {
+ void activityDismissingSplitScreen() {
Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
Toast.LENGTH_SHORT).show();
}
- private void activityLaunchOnSecondaryDisplayFailed() {
+ void activityLaunchOnSecondaryDisplayFailed() {
Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text,
Toast.LENGTH_SHORT).show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b2cfcea..8cb54ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -662,16 +662,18 @@
mIconController.setIconVisibility(mSlotCamera, showCamera);
mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
- mIconController.setIconVisibility(mSlotLocation, showLocation);
+ if (mPrivacyItemController.getAllIndicatorsAvailable()) {
+ mIconController.setIconVisibility(mSlotLocation, showLocation);
+ }
}
@Override
public void onLocationActiveChanged(boolean active) {
- if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
+ if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
}
// Updates the status view based on the current state of location requests.
- private void updateLocation() {
+ private void updateLocationFromController() {
if (mLocationController.isLocationActive()) {
mIconController.setIconVisibility(mSlotLocation, true);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index b89cb21..8ff7a41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -86,7 +86,7 @@
static ArraySet<String> getIconHideList(Context context, String hideListStr) {
ArraySet<String> ret = new ArraySet<>();
String[] hideList = hideListStr == null
- ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
+ ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
: hideListStr.split(",");
for (String slot : hideList) {
if (!TextUtils.isEmpty(slot)) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index fbc8e9d..ac567e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -25,6 +25,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.Display;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IWindowMagnificationConnection;
@@ -47,6 +48,7 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class IWindowMagnificationConnectionTest extends SysuiTestCase {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@@ -57,7 +59,7 @@
@Mock
private IWindowMagnificationConnectionCallback mConnectionCallback;
@Mock
- private WindowMagnificationController mWindowMagnificationController;
+ private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@Mock
private ModeSwitchesController mModeSwitchesController;
private IWindowMagnificationConnection mIWindowMagnificationConnection;
@@ -74,7 +76,8 @@
any(IWindowMagnificationConnection.class));
mWindowMagnification = new WindowMagnification(getContext(),
getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController);
- mWindowMagnification.mWindowMagnificationController = mWindowMagnificationController;
+ mWindowMagnification.mWindowMagnificationAnimationController =
+ mWindowMagnificationAnimationController;
mWindowMagnification.requestWindowMagnificationConnection(true);
assertNotNull(mIWindowMagnificationConnection);
mIWindowMagnificationConnection.setConnectionCallback(mConnectionCallback);
@@ -86,7 +89,7 @@
Float.NaN);
waitForIdleSync();
- verify(mWindowMagnificationController).enableWindowMagnification(3.0f, Float.NaN,
+ verify(mWindowMagnificationAnimationController).enableWindowMagnification(3.0f, Float.NaN,
Float.NaN);
}
@@ -99,7 +102,7 @@
mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY);
waitForIdleSync();
- verify(mWindowMagnificationController).deleteWindowMagnification();
+ verify(mWindowMagnificationAnimationController).deleteWindowMagnification();
}
@Test
@@ -107,7 +110,7 @@
mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
waitForIdleSync();
- verify(mWindowMagnificationController).setScale(3.0f);
+ verify(mWindowMagnificationAnimationController).setScale(3.0f);
}
@Test
@@ -115,7 +118,7 @@
mIWindowMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
waitForIdleSync();
- verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f);
+ verify(mWindowMagnificationAnimationController).moveWindowMagnifier(100f, 200f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
new file mode 100644
index 0000000..add0843
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -0,0 +1,362 @@
+/*
+ * 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.systemui.accessibility;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.ValueAnimator;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.SurfaceControl;
+import android.view.animation.AccelerateInterpolator;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+
+@MediumTest
+@RunWith(AndroidTestingRunner.class)
+public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
+
+ private static final float DEFAULT_SCALE = 3.0f;
+ private static final float DEFAULT_CENTER_X = 400.0f;
+ private static final float DEFAULT_CENTER_Y = 500.0f;
+ private static final long ANIMATION_DURATION_MS = 100;
+
+ private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
+ private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
+ private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0);
+ private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
+ private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
+ private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
+
+ @Mock
+ Handler mHandler;
+ @Mock
+ SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+ @Mock
+ WindowMagnifierCallback mWindowMagnifierCallback;
+
+ private SpyWindowMagnificationController mController;
+ private WindowMagnificationController mSpyController;
+ private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+ private Instrumentation mInstrumentation;
+ private long mWaitingAnimationPeriod;
+ private long mWaitIntermediateAnimationPeriod;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mWaitingAnimationPeriod = ANIMATION_DURATION_MS + 50;
+ mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
+ mController = new SpyWindowMagnificationController(mContext, mHandler,
+ mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
+ mWindowMagnifierCallback);
+ mSpyController = mController.getSpyController();
+ mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+ mContext, mController, newValueAnimator());
+ }
+
+ @Test
+ public void enableWindowMagnification_disabled_expectedStartAndEndValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verifyStartValue(mScaleCaptor, 1.0f);
+ verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
+ verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
+ verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ }
+
+ @Test
+ public void enableWindowMagnification_enabling_expectedStartAndEndValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+ final float targetScale = DEFAULT_SCALE + 1.0f;
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+ mInstrumentation.runOnMainSync(() -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ });
+
+ SystemClock.sleep(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verifyStartValue(mScaleCaptor, mCurrentScale.get());
+ verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
+ verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+ verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+ }
+
+ @Test
+ public void enableWindowMagnification_disabling_expectedStartAndEndValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+ deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+ final float targetScale = DEFAULT_SCALE + 1.0f;
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ });
+ SystemClock.sleep(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ //Animating in reverse, so we only check if the start values are greater than current.
+ assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
+ assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
+ assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
+ assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
+ assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
+ assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
+ verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+ }
+
+ @Test
+ public void enableWindowMagnificationWithSameScale_doNothing() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+ anyFloat());
+ }
+
+ @Test
+ public void setScale_enabled_expectedScale() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
+
+ verify(mSpyController).setScale(DEFAULT_SCALE + 1);
+ verifyFinalSpec(DEFAULT_SCALE + 1, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ }
+
+ @Test
+ public void deleteWindowMagnification_enabled_expectedStartAndEndValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController).deleteWindowMagnification();
+ verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
+ verifyStartValue(mCenterXCaptor, Float.NaN);
+ verifyStartValue(mCenterYCaptor, Float.NaN);
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+ }
+
+ @Test
+ public void deleteWindowMagnification_disabled_doNothing() {
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ Mockito.verifyNoMoreInteractions(mSpyController);
+ }
+
+ @Test
+ public void deleteWindowMagnification_enabling_checkStartAndEndValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+
+ //It just reverse the animation, so we don't need to wait the whole duration.
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.deleteWindowMagnification();
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ });
+ SystemClock.sleep(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController).deleteWindowMagnification();
+
+ //The animation is in verse, so we only check the start values should no be greater than
+ // the current one.
+ assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
+ assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+ verifyStartValue(mCenterXCaptor, Float.NaN);
+ verifyStartValue(mCenterYCaptor, Float.NaN);
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+ }
+
+ @Test
+ public void deleteWindowMagnification_disabling_checkStartAndValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+ deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController).deleteWindowMagnification();
+ assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+ }
+
+ @Test
+ public void moveWindowMagnifier_enabled() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
+
+ verify(mSpyController).moveWindowMagnifier(100f, 200f);
+ verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f);
+ }
+
+ @Test
+ public void onConfigurationChanged_passThrough() {
+ mWindowMagnificationAnimationController.onConfigurationChanged(100);
+
+ verify(mSpyController).onConfigurationChanged(100);
+ }
+ private void verifyFinalSpec(float expectedScale, float expectedCenterX,
+ float expectedCenterY) {
+ assertEquals(expectedScale, mController.getScale(), 0f);
+ assertEquals(expectedCenterX, mController.getCenterX(), 0f);
+ assertEquals(expectedCenterY, mController.getCenterY(), 0f);
+ }
+
+ private void enableWindowMagnificationAndWaitAnimating(long duration) {
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+ DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ });
+ SystemClock.sleep(duration);
+ }
+
+ private void deleteWindowMagnificationAndWaitAnimating(long duration) {
+ mInstrumentation.runOnMainSync(
+ () -> {
+ resetMockObjects();
+ mWindowMagnificationAnimationController.deleteWindowMagnification();
+ });
+ SystemClock.sleep(duration);
+ }
+
+ private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
+ assertEquals(startValue, captor.getAllValues().get(0), 0f);
+ }
+
+ private void resetMockObjects() {
+ Mockito.reset(mSpyController);
+ }
+
+ /**
+ * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it
+ * directly.
+ */
+ private static class SpyWindowMagnificationController extends WindowMagnificationController {
+ private WindowMagnificationController mSpyController;
+
+ SpyWindowMagnificationController(Context context, Handler handler,
+ SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
+ MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
+ WindowMagnifierCallback callback) {
+ super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction,
+ callback);
+ mSpyController = Mockito.mock(WindowMagnificationController.class);
+ }
+
+ WindowMagnificationController getSpyController() {
+ return mSpyController;
+ }
+
+ @Override
+ void enableWindowMagnification(float scale, float centerX, float centerY) {
+ super.enableWindowMagnification(scale, centerX, centerY);
+ mSpyController.enableWindowMagnification(scale, centerX, centerY);
+ }
+
+ @Override
+ void deleteWindowMagnification() {
+ super.deleteWindowMagnification();
+ mSpyController.deleteWindowMagnification();
+ }
+
+ @Override
+ void moveWindowMagnifier(float offsetX, float offsetY) {
+ super.moveWindowMagnifier(offsetX, offsetX);
+ mSpyController.moveWindowMagnifier(offsetX, offsetY);
+ }
+
+ @Override
+ void setScale(float scale) {
+ super.setScale(scale);
+ mSpyController.setScale(scale);
+ }
+
+ @Override
+ void onConfigurationChanged(int configDiff) {
+ super.onConfigurationChanged(configDiff);
+ mSpyController.onConfigurationChanged(configDiff);
+ }
+
+ }
+
+ private static ValueAnimator newValueAnimator() {
+ final ValueAnimator valueAnimator = new ValueAnimator();
+ valueAnimator.setDuration(ANIMATION_DURATION_MS);
+ valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
+ valueAnimator.setFloatValues(0.0f, 1.0f);
+ return valueAnimator;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 2007fbb..1515cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -18,6 +18,7 @@
import static android.view.Choreographer.FrameCallback;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
@@ -83,9 +84,8 @@
@After
public void tearDown() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.deleteWindowMagnification();
- });
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.deleteWindowMagnification());
}
@Test
@@ -121,4 +121,18 @@
verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
}
+
+ @Test
+ public void setScale_expectedValue() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.setScale(3.0f);
+ });
+
+ assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 4136013..936558b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.Display;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IWindowMagnificationConnection;
@@ -45,6 +46,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class WindowMagnificationTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
new file mode 100644
index 0000000..4ba29e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.systemui.privacy
+
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PrivacyItemControllerFlagsTest : SysuiTestCase() {
+ companion object {
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ fun <T> any(): T = Mockito.any<T>()
+
+ private const val ALL_INDICATORS =
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+ private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+ }
+
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+ @Mock
+ private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var dumpManager: DumpManager
+
+ private lateinit var privacyItemController: PrivacyItemController
+ private lateinit var executor: FakeExecutor
+ private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+ fun PrivacyItemController(): PrivacyItemController {
+ return PrivacyItemController(
+ appOpsController,
+ executor,
+ executor,
+ broadcastDispatcher,
+ deviceConfigProxy,
+ userManager,
+ dumpManager
+ )
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+ deviceConfigProxy = DeviceConfigProxyFake()
+
+ privacyItemController = PrivacyItemController()
+ privacyItemController.addCallback(callback)
+
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testNotListeningByDefault() {
+ assertFalse(privacyItemController.allIndicatorsAvailable)
+ assertFalse(privacyItemController.micCameraAvailable)
+
+ verify(appOpsController, never()).addCallback(any(), any())
+ }
+
+ @Test
+ fun testMicCameraChanged() {
+ changeMicCamera(true)
+ executor.runAllReady()
+
+ verify(callback).onFlagMicCameraChanged(true)
+ verify(callback, never()).onFlagAllChanged(anyBoolean())
+
+ assertTrue(privacyItemController.micCameraAvailable)
+ assertFalse(privacyItemController.allIndicatorsAvailable)
+ }
+
+ @Test
+ fun testAllChanged() {
+ changeAll(true)
+ executor.runAllReady()
+
+ verify(callback).onFlagAllChanged(true)
+ verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
+
+ assertTrue(privacyItemController.allIndicatorsAvailable)
+ assertFalse(privacyItemController.micCameraAvailable)
+ }
+
+ @Test
+ fun testBothChanged() {
+ changeAll(true)
+ changeMicCamera(true)
+ executor.runAllReady()
+
+ verify(callback, atLeastOnce()).onFlagAllChanged(true)
+ verify(callback, atLeastOnce()).onFlagMicCameraChanged(true)
+
+ assertTrue(privacyItemController.allIndicatorsAvailable)
+ assertTrue(privacyItemController.micCameraAvailable)
+ }
+
+ @Test
+ fun testAll_listeningToAll() {
+ changeAll(true)
+ executor.runAllReady()
+
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+ }
+
+ @Test
+ fun testMicCamera_listening() {
+ changeMicCamera(true)
+ executor.runAllReady()
+
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+ }
+
+ @Test
+ fun testAll_listening() {
+ changeAll(true)
+ executor.runAllReady()
+
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+ }
+
+ @Test
+ fun testAllFalse_notListening() {
+ changeAll(true)
+ executor.runAllReady()
+ changeAll(false)
+ executor.runAllReady()
+
+ verify(appOpsController).removeCallback(any(), any())
+ }
+
+ @Test
+ fun testSomeListening_stillListening() {
+ changeAll(true)
+ changeMicCamera(true)
+ executor.runAllReady()
+ changeAll(false)
+ executor.runAllReady()
+
+ verify(appOpsController, never()).removeCallback(any(), any())
+ }
+
+ @Test
+ fun testAllDeleted_stopListening() {
+ changeAll(true)
+ executor.runAllReady()
+ changeAll(null)
+ executor.runAllReady()
+
+ verify(appOpsController).removeCallback(any(), any())
+ }
+
+ @Test
+ fun testMicDeleted_stopListening() {
+ changeMicCamera(true)
+ executor.runAllReady()
+ changeMicCamera(null)
+ executor.runAllReady()
+
+ verify(appOpsController).removeCallback(any(), any())
+ }
+
+ private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+ private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+
+ private fun changeProperty(name: String, value: Boolean?) {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ name,
+ value?.toString(),
+ false
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index dddc350..fb42baa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,7 +18,6 @@
import android.app.ActivityManager
import android.app.AppOpsManager
-import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
import android.os.UserHandle
@@ -69,10 +68,11 @@
companion object {
val CURRENT_USER_ID = ActivityManager.getCurrentUser()
val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
- const val SYSTEM_UID = 1000
const val TEST_PACKAGE_NAME = "test"
- const val DEVICE_SERVICES_STRING = "Device services"
- const val TAG = "PrivacyItemControllerTest"
+
+ private const val ALL_INDICATORS =
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+ private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
fun <T> any(): T = Mockito.any<T>()
@@ -97,9 +97,8 @@
private lateinit var executor: FakeExecutor
private lateinit var deviceConfigProxy: DeviceConfigProxy
- fun PrivacyItemController(context: Context): PrivacyItemController {
+ fun PrivacyItemController(): PrivacyItemController {
return PrivacyItemController(
- context,
appOpsController,
executor,
executor,
@@ -116,11 +115,8 @@
executor = FakeExecutor(FakeSystemClock())
deviceConfigProxy = DeviceConfigProxyFake()
- appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-
- deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
- "true", false)
+ // Listen to everything by default
+ changeAll(true)
doReturn(listOf(object : UserInfo() {
init {
@@ -128,7 +124,7 @@
}
})).`when`(userManager).getProfiles(anyInt())
- privacyItemController = PrivacyItemController(mContext)
+ privacyItemController = PrivacyItemController()
}
@Test
@@ -276,15 +272,59 @@
@Test
fun testNotListeningWhenIndicatorsDisabled() {
- deviceConfigProxy.setProperty(
- DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
- "false",
- false
- )
+ changeAll(false)
privacyItemController.addCallback(callback)
executor.runAllReady()
verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
any())
}
+
+ @Test
+ fun testNotSendingLocationWhenOnlyMicCamera() {
+ changeAll(false)
+ changeMicCamera(true)
+ executor.runAllReady()
+
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+
+ assertEquals(1, argCaptor.value.size)
+ assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType)
+ }
+
+ @Test
+ fun testNotUpdated_LocationChangeWhenOnlyMicCamera() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.addCallback(callback)
+ changeAll(false)
+ changeMicCamera(true)
+ executor.runAllReady()
+ reset(callback) // Clean callback
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+
+ verify(callback, never()).onPrivacyItemsChanged(any())
+ }
+
+ private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+ private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+
+ private fun changeProperty(name: String, value: Boolean?) {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ name,
+ value?.toString(),
+ false
+ )
+ }
}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index bd25f2b..3ee5b28 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -25,10 +25,12 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
+import android.view.Display;
import android.view.MotionEvent;
import com.android.internal.annotations.VisibleForTesting;
@@ -90,6 +92,8 @@
private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
private final int mDisplayId;
+ private final Context mContext;
+ private final Point mTempPoint = new Point();
private final Queue<MotionEvent> mDebugOutputEventHistory;
@@ -107,7 +111,7 @@
Slog.i(LOG_TAG,
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
}
-
+ mContext = context;
mWindowMagnificationMgr = windowMagnificationMgr;
mDetectShortcutTrigger = detectShortcutTrigger;
mDisplayId = displayId;
@@ -184,7 +188,14 @@
if (!mDetectShortcutTrigger) {
return;
}
- toggleMagnification(Float.NaN, Float.NaN);
+ final Point screenSize = mTempPoint;
+ getScreenSize(mTempPoint);
+ toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f);
+ }
+
+ private void getScreenSize(Point outSize) {
+ final Display display = mContext.getDisplay();
+ display.getRealSize(outSize);
}
@Override
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 7cf5fd6..b7fed87 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -50,5 +50,5 @@
* Informs battery stats of binder stats for the given work source UID.
*/
public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
- Collection<BinderCallsStats.CallStat> callStats);
+ Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e546a28..91a1487 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1956,7 +1956,7 @@
nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
}
memInfo.readMemInfo();
- synchronized (ActivityManagerService.this) {
+ 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();
@@ -7048,9 +7048,7 @@
mUsageStatsService.prepareShutdown();
}
mBatteryStatsService.shutdown();
- synchronized (this) {
- mProcessStats.shutdownLocked();
- }
+ mProcessStats.shutdown();
return timedout;
}
@@ -12352,7 +12350,7 @@
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
if (nativeProcTotalPss > 0) {
- synchronized (this) {
+ synchronized (mProcessStats.mLock) {
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
@@ -12934,7 +12932,7 @@
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
if (nativeProcTotalPss > 0) {
- synchronized (this) {
+ synchronized (mProcessStats.mLock) {
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
@@ -16505,9 +16503,7 @@
}
@Override public void run() {
- synchronized (mService) {
- mProcessStats.writeStateAsyncLocked();
- }
+ mProcessStats.writeStateAsync();
}
}
@@ -16557,9 +16553,13 @@
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mProcessList.getLruSizeLocked();
- boolean allChanged = mProcessStats.setMemFactorLocked(
- memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
- final int trackerMemFactor = mProcessStats.getMemFactorLocked();
+ 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;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5081b36..d72998b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -227,8 +227,9 @@
@Override
public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
- mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats);
+ Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
+ mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats,
+ binderThreadNativeTids);
}
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 8970ec4..0f2dfcc 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -116,6 +116,10 @@
WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL,
+ WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, int.class,
+ WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE,
WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class,
WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT));
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f0343e1..bf15f1737 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -659,13 +659,15 @@
updateUidsLocked(activeUids, nowElapsed);
- if (mService.mProcessStats.shouldWriteNowLocked(now)) {
- mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
- mService.mProcessStats));
- }
+ synchronized (mService.mProcessStats.mLock) {
+ if (mService.mProcessStats.shouldWriteNowLocked(now)) {
+ mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
+ mService.mProcessStats));
+ }
- // Run this after making sure all procstates are updated.
- mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+ // Run this after making sure all procstates are updated.
+ mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+ }
if (DEBUG_OOM_ADJ) {
final long duration = SystemClock.uptimeMillis() - now;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 1647fda..e3e1339 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -673,30 +673,33 @@
public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
if (thread == null) {
- final ProcessState origBase = baseProcessTracker;
- if (origBase != null) {
- origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- pkgList.valueAt(ipkg).appVersion);
+ synchronized (tracker.mLock) {
+ final ProcessState origBase = baseProcessTracker;
+ if (origBase != null) {
+ origBase.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+ pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
+ origBase.makeInactive();
}
- origBase.makeInactive();
- }
- baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
- info.longVersionCode, processName);
- baseProcessTracker.makeActive();
- for (int i=0; i<pkgList.size(); i++) {
- ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
- if (holder.state != null && holder.state != origBase) {
- holder.state.makeInactive();
- }
- tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
+ baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
info.longVersionCode, processName);
- if (holder.state != baseProcessTracker) {
- holder.state.makeActive();
+ baseProcessTracker.makeActive();
+ for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
+ }
+ tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
+ info.longVersionCode, processName);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
+ }
}
}
}
@@ -707,27 +710,30 @@
public void makeInactive(ProcessStatsService tracker) {
thread = null;
mWindowProcessController.setThread(null);
- final ProcessState origBase = baseProcessTracker;
- if (origBase != null) {
+ synchronized (tracker.mLock) {
+ final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
- origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- pkgList.valueAt(ipkg).appVersion);
+ if (origBase != null) {
+ origBase.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+ pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
+ origBase.makeInactive();
}
- origBase.makeInactive();
- }
- baseProcessTracker = null;
- for (int i=0; i<pkgList.size(); i++) {
- ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
- if (holder.state != null && holder.state != origBase) {
- holder.state.makeInactive();
+ baseProcessTracker = null;
+ for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
+ }
+ holder.pkg = null;
+ holder.state = null;
}
- holder.pkg = null;
- holder.state = null;
}
}
}
@@ -1026,17 +1032,19 @@
*/
public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) {
if (!pkgList.containsKey(pkg)) {
- ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
- versionCode);
- if (baseProcessTracker != null) {
- tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
- processName);
- pkgList.put(pkg, holder);
- if (holder.state != baseProcessTracker) {
- holder.state.makeActive();
+ synchronized (tracker.mLock) {
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ versionCode);
+ if (baseProcessTracker != null) {
+ tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
+ processName);
+ pkgList.put(pkg, holder);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
+ }
+ } else {
+ pkgList.put(pkg, holder);
}
- } else {
- pkgList.put(pkg, holder);
}
return true;
}
@@ -1072,31 +1080,33 @@
public void resetPackageList(ProcessStatsService tracker) {
final int N = pkgList.size();
if (baseProcessTracker != null) {
- long now = SystemClock.uptimeMillis();
- baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), now, pkgList.mPkgList);
- for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgList.keyAt(ipkg),
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- pkgList.valueAt(ipkg).appVersion);
- }
- if (N != 1) {
- for (int i=0; i<N; i++) {
- ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
- if (holder.state != null && holder.state != baseProcessTracker) {
- holder.state.makeInactive();
- }
-
+ synchronized (tracker.mLock) {
+ long now = SystemClock.uptimeMillis();
+ baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), now, pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
}
- pkgList.clear();
- ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
- info.longVersionCode);
- tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
- info.longVersionCode, processName);
- pkgList.put(info.packageName, holder);
- if (holder.state != baseProcessTracker) {
- holder.state.makeActive();
+ if (N != 1) {
+ for (int i = 0; i < N; i++) {
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != baseProcessTracker) {
+ holder.state.makeInactive();
+ }
+
+ }
+ pkgList.clear();
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ info.longVersionCode);
+ tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
+ info.longVersionCode, processName);
+ pkgList.put(info.packageName, holder);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
+ }
}
}
} else if (N != 1) {
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index a168af5a..4e8c386 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -71,33 +71,62 @@
final ActivityManagerService mAm;
final File mBaseDir;
- ProcessStats mProcessStats;
+
+ // Note: The locking order of the below 3 locks should be:
+ // mLock, mPendingWriteLock, mFileLock
+
+ // The lock object to protect the internal state/structures
+ final Object mLock = new Object();
+
+ // The lock object to protect the access to pending writes
+ final Object mPendingWriteLock = new Object();
+
+ // The lock object to protect the access to all of the file read/write
+ final ReentrantLock mFileLock = new ReentrantLock();
+
+ @GuardedBy("mLock")
+ final ProcessStats mProcessStats;
+
+ @GuardedBy("mFileLock")
AtomicFile mFile;
+
+ @GuardedBy("mLock")
boolean mCommitPending;
+
+ @GuardedBy("mLock")
boolean mShuttingDown;
+
+ @GuardedBy("mLock")
int mLastMemOnlyState = -1;
boolean mMemFactorLowered;
- final ReentrantLock mWriteLock = new ReentrantLock();
- final Object mPendingWriteLock = new Object();
+ @GuardedBy("mPendingWriteLock")
AtomicFile mPendingWriteFile;
+
+ @GuardedBy("mPendingWriteLock")
Parcel mPendingWrite;
+
+ @GuardedBy("mPendingWriteLock")
boolean mPendingWriteCommitted;
+
+ @GuardedBy("mLock")
long mLastWriteTime;
/** For CTS to inject the screen state. */
- @GuardedBy("mAm")
+ @GuardedBy("mLock")
Boolean mInjectedScreenState;
public ProcessStatsService(ActivityManagerService am, File file) {
mAm = am;
mBaseDir = file;
mBaseDir.mkdirs();
- mProcessStats = new ProcessStats(true);
- updateFile();
+ synchronized (mLock) {
+ mProcessStats = new ProcessStats(true);
+ updateFileLocked();
+ }
SystemProperties.addChangeCallback(new Runnable() {
@Override public void run() {
- synchronized (mAm) {
+ synchronized (mLock) {
if (mProcessStats.evaluateSystemProperties(false)) {
mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
writeStateLocked(true, true);
@@ -121,32 +150,33 @@
}
}
- @GuardedBy("mAm")
- public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
+ @GuardedBy("mLock")
+ void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
String packageName, int uid, long versionCode, String processName) {
holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode);
holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName);
}
- @GuardedBy("mAm")
- public ProcessState getProcessStateLocked(String packageName,
+ @GuardedBy("mLock")
+ ProcessState getProcessStateLocked(String packageName,
int uid, long versionCode, String processName) {
return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
}
- @GuardedBy("mAm")
- public ServiceState getServiceStateLocked(String packageName, int uid,
+ ServiceState getServiceState(String packageName, int uid,
long versionCode, String processName, String className) {
- return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
- className);
+ synchronized (mLock) {
+ return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
+ className);
+ }
}
- public boolean isMemFactorLowered() {
+ boolean isMemFactorLowered() {
return mMemFactorLowered;
}
- @GuardedBy("mAm")
- public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
+ @GuardedBy("mLock")
+ boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
mMemFactorLowered = memFactor < mLastMemOnlyState;
mLastMemOnlyState = memFactor;
if (mInjectedScreenState != null) {
@@ -184,24 +214,24 @@
return false;
}
- @GuardedBy("mAm")
- public int getMemFactorLocked() {
+ @GuardedBy("mLock")
+ int getMemFactorLocked() {
return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
}
- @GuardedBy("mAm")
- public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
+ @GuardedBy("mLock")
+ void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
long nativeMem) {
mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
}
- @GuardedBy("mAm")
- public void updateTrackingAssociationsLocked(int curSeq, long now) {
+ @GuardedBy("mLock")
+ void updateTrackingAssociationsLocked(int curSeq, long now) {
mProcessStats.updateTrackingAssociationsLocked(curSeq, now);
}
- @GuardedBy("mAm")
- public boolean shouldWriteNowLocked(long now) {
+ @GuardedBy("mLock")
+ boolean shouldWriteNowLocked(long now) {
if (now > (mLastWriteTime+WRITE_PERIOD)) {
if (SystemClock.elapsedRealtime()
> (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
@@ -214,25 +244,27 @@
return false;
}
- @GuardedBy("mAm")
- public void shutdownLocked() {
+ void shutdown() {
Slog.w(TAG, "Writing process stats before shutdown...");
- mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
- writeStateSyncLocked();
- mShuttingDown = true;
+ synchronized (mLock) {
+ mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
+ writeStateSyncLocked();
+ mShuttingDown = true;
+ }
}
- @GuardedBy("mAm")
- public void writeStateAsyncLocked() {
- writeStateLocked(false);
+ void writeStateAsync() {
+ synchronized (mLock) {
+ writeStateLocked(false);
+ }
}
- @GuardedBy("mAm")
- public void writeStateSyncLocked() {
+ @GuardedBy("mLock")
+ private void writeStateSyncLocked() {
writeStateLocked(true);
}
- @GuardedBy("mAm")
+ @GuardedBy("mLock")
private void writeStateLocked(boolean sync) {
if (mShuttingDown) {
return;
@@ -242,8 +274,8 @@
writeStateLocked(sync, commitPending);
}
- @GuardedBy("mAm")
- public void writeStateLocked(boolean sync, final boolean commit) {
+ @GuardedBy("mLock")
+ private void writeStateLocked(boolean sync, final boolean commit) {
final long totalTime;
synchronized (mPendingWriteLock) {
final long now = SystemClock.uptimeMillis();
@@ -255,13 +287,13 @@
mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
}
mProcessStats.writeToParcel(mPendingWrite, 0);
- mPendingWriteFile = new AtomicFile(mFile.getBaseFile());
+ mPendingWriteFile = new AtomicFile(getCurrentFile());
mPendingWriteCommitted = commit;
}
if (commit) {
mProcessStats.resetSafely();
- updateFile();
- mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+ updateFileLocked();
+ scheduleRequestPssAllProcs(true, false);
}
mLastWriteTime = SystemClock.uptimeMillis();
totalTime = SystemClock.uptimeMillis() - now;
@@ -279,14 +311,37 @@
performWriteState(totalTime);
}
- private void updateFile() {
- mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
- + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+ private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
+ mAm.mHandler.post(() -> {
+ synchronized (mAm) {
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered);
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
+ private void updateFileLocked() {
+ mFileLock.lock();
+ try {
+ mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
+ + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+ } finally {
+ mFileLock.unlock();
+ }
mLastWriteTime = SystemClock.uptimeMillis();
}
- void performWriteState(long initialTime) {
- if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile());
+ private File getCurrentFile() {
+ mFileLock.lock();
+ try {
+ return mFile.getBaseFile();
+ } finally {
+ mFileLock.unlock();
+ }
+ }
+
+ private void performWriteState(long initialTime) {
+ if (DEBUG) Slog.d(TAG, "Performing write to " + getCurrentFile());
Parcel data;
AtomicFile file;
synchronized (mPendingWriteLock) {
@@ -298,7 +353,7 @@
}
mPendingWrite = null;
mPendingWriteFile = null;
- mWriteLock.lock();
+ mFileLock.lock();
}
final long startTime = SystemClock.uptimeMillis();
@@ -316,13 +371,13 @@
file.failWrite(stream);
} finally {
data.recycle();
- trimHistoricStatesWriteLocked();
- mWriteLock.unlock();
+ trimHistoricStatesWriteLF();
+ mFileLock.unlock();
}
}
- @GuardedBy("mAm")
- boolean readLocked(ProcessStats stats, AtomicFile file) {
+ @GuardedBy("mFileLock")
+ private boolean readLF(ProcessStats stats, AtomicFile file) {
try {
FileInputStream stream = file.openRead();
stats.read(stream);
@@ -387,7 +442,8 @@
return true;
}
- private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent,
+ @GuardedBy("mFileLock")
+ private ArrayList<String> getCommittedFilesLF(int minNum, boolean inclCurrent,
boolean inclCheckedIn) {
File[] files = mBaseDir.listFiles();
if (files == null || files.length <= minNum) {
@@ -414,9 +470,9 @@
return filesArray;
}
- @GuardedBy("mAm")
- public void trimHistoricStatesWriteLocked() {
- ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
+ @GuardedBy("mFileLock")
+ private void trimHistoricStatesWriteLF() {
+ ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true);
if (filesArray == null) {
return;
}
@@ -427,8 +483,8 @@
}
}
- @GuardedBy("mAm")
- boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
+ @GuardedBy("mLock")
+ private boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now, String reqPackage) {
ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked(
@@ -502,20 +558,21 @@
return res;
}
+ @Override
public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
mAm.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS, null);
Parcel current = Parcel.obtain();
- synchronized (mAm) {
+ synchronized (mLock) {
long now = SystemClock.uptimeMillis();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
mProcessStats.mTimePeriodEndUptime = now;
mProcessStats.writeToParcel(current, now, 0);
}
- mWriteLock.lock();
+ mFileLock.lock();
try {
if (historic != null) {
- ArrayList<String> files = getCommittedFiles(0, false, true);
+ ArrayList<String> files = getCommittedFilesLF(0, false, true);
if (files != null) {
for (int i=files.size()-1; i>=0; i--) {
try {
@@ -529,7 +586,7 @@
}
}
} finally {
- mWriteLock.unlock();
+ mFileLock.unlock();
}
return current.marshall();
}
@@ -563,9 +620,9 @@
android.Manifest.permission.PACKAGE_USAGE_STATS, null);
long newHighWaterMark = highWaterMarkMs;
- mWriteLock.lock();
+ mFileLock.lock();
try {
- ArrayList<String> files = getCommittedFiles(0, false, true);
+ ArrayList<String> files = getCommittedFilesLF(0, false, true);
if (files != null) {
String highWaterMarkStr =
DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString();
@@ -612,7 +669,7 @@
} catch (IOException e) {
Slog.w(TAG, "Failure opening procstat file", e);
} finally {
- mWriteLock.unlock();
+ mFileLock.unlock();
}
return newHighWaterMark;
}
@@ -625,7 +682,7 @@
return mAm.mConstants.MIN_ASSOC_LOG_DURATION;
}
- private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
+ private static ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
throws IOException {
final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
Thread thr = new Thread("ProcessStats pipe output") {
@@ -645,12 +702,13 @@
return fds[0];
}
+ @Override
public ParcelFileDescriptor getStatsOverTime(long minTime) {
mAm.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS, null);
Parcel current = Parcel.obtain();
long curTime;
- synchronized (mAm) {
+ synchronized (mLock) {
long now = SystemClock.uptimeMillis();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
mProcessStats.mTimePeriodEndUptime = now;
@@ -658,11 +716,11 @@
curTime = mProcessStats.mTimePeriodEndRealtime
- mProcessStats.mTimePeriodStartRealtime;
}
- mWriteLock.lock();
+ mFileLock.lock();
try {
if (curTime < minTime) {
// Need to add in older stats to reach desired time.
- ArrayList<String> files = getCommittedFiles(0, false, true);
+ ArrayList<String> files = getCommittedFilesLF(0, false, true);
if (files != null && files.size() > 0) {
current.setDataPosition(0);
ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
@@ -673,7 +731,7 @@
AtomicFile file = new AtomicFile(new File(files.get(i)));
i--;
ProcessStats moreStats = new ProcessStats(false);
- readLocked(moreStats, file);
+ readLF(moreStats, file);
if (moreStats.mReadError == null) {
stats.add(moreStats);
StringBuilder sb = new StringBuilder();
@@ -712,13 +770,14 @@
} catch (IOException e) {
Slog.w(TAG, "Failed building output pipe", e);
} finally {
- mWriteLock.unlock();
+ mFileLock.unlock();
}
return null;
}
+ @Override
public int getCurrentMemoryState() {
- synchronized (mAm) {
+ synchronized (mLock) {
return mLastMemOnlyState;
}
}
@@ -947,7 +1006,7 @@
} else if ("--current".equals(arg)) {
currentOnly = true;
} else if ("--commit".equals(arg)) {
- synchronized (mAm) {
+ synchronized (mLock) {
mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
writeStateLocked(true, true);
pw.println("Process stats committed.");
@@ -962,29 +1021,39 @@
}
section = parseSectionOptions(args[i]);
} else if ("--clear".equals(arg)) {
- synchronized (mAm) {
+ synchronized (mLock) {
mProcessStats.resetSafely();
- mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
- ArrayList<String> files = getCommittedFiles(0, true, true);
- if (files != null) {
- for (int fi=0; fi<files.size(); fi++) {
- (new File(files.get(fi))).delete();
+ scheduleRequestPssAllProcs(true, false);
+ mFileLock.lock();
+ try {
+ ArrayList<String> files = getCommittedFilesLF(0, true, true);
+ if (files != null) {
+ for (int fi = files.size() - 1; fi >= 0; fi--) {
+ (new File(files.get(fi))).delete();
+ }
}
+ } finally {
+ mFileLock.unlock();
}
pw.println("All process stats cleared.");
quit = true;
}
} else if ("--write".equals(arg)) {
- synchronized (mAm) {
+ synchronized (mLock) {
writeStateSyncLocked();
pw.println("Process stats written.");
quit = true;
}
} else if ("--read".equals(arg)) {
- synchronized (mAm) {
- readLocked(mProcessStats, mFile);
- pw.println("Process stats read.");
- quit = true;
+ synchronized (mLock) {
+ mFileLock.lock();
+ try {
+ readLF(mProcessStats, mFile);
+ pw.println("Process stats read.");
+ quit = true;
+ } finally {
+ mFileLock.unlock();
+ }
}
} else if ("--start-testing".equals(arg)) {
synchronized (mAm) {
@@ -999,17 +1068,17 @@
quit = true;
}
} else if ("--pretend-screen-on".equals(arg)) {
- synchronized (mAm) {
+ synchronized (mLock) {
mInjectedScreenState = true;
}
quit = true;
} else if ("--pretend-screen-off".equals(arg)) {
- synchronized (mAm) {
+ synchronized (mLock) {
mInjectedScreenState = false;
}
quit = true;
} else if ("--stop-pretend-screen".equals(arg)) {
- synchronized (mAm) {
+ synchronized (mLock) {
mInjectedScreenState = null;
}
quit = true;
@@ -1060,7 +1129,7 @@
}
}
pw.println();
- synchronized (mAm) {
+ synchronized (mLock) {
dumpFilteredProcessesCsvLocked(pw, null,
csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
csvSepProcStats, csvProcStats, now, reqPackage);
@@ -1090,14 +1159,26 @@
return;
} else if (lastIndex > 0) {
pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
- ArrayList<String> files = getCommittedFiles(0, false, true);
- if (lastIndex >= files.size()) {
- pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
- return;
+
+ ArrayList<String> files;
+ AtomicFile file;
+ ProcessStats processStats;
+
+ mFileLock.lock();
+ try {
+ files = getCommittedFilesLF(0, false, true);
+ if (lastIndex >= files.size()) {
+ pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
+ return;
+ }
+ file = new AtomicFile(new File(files.get(lastIndex)));
+ processStats = new ProcessStats(false);
+ readLF(processStats, file);
+ } finally {
+ mFileLock.unlock();
}
- AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
- ProcessStats processStats = new ProcessStats(false);
- readLocked(processStats, file);
+
+ // No lock is needed now, since only us have the access to the 'processStats'.
if (processStats.mReadError != null) {
if (isCheckin || isCompact) pw.print("err,");
pw.print("Failure reading "); pw.print(files.get(lastIndex));
@@ -1118,7 +1199,7 @@
processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
dumpAll, activeOnly, section);
if (dumpAll) {
- pw.print(" mFile="); pw.println(mFile.getBaseFile());
+ pw.print(" mFile="); pw.println(getCurrentFile());
}
} else {
processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1129,9 +1210,9 @@
boolean sepNeeded = false;
if ((dumpAll || isCheckin) && !currentOnly) {
- mWriteLock.lock();
+ mFileLock.lock();
try {
- ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
+ ArrayList<String> files = getCommittedFilesLF(0, false, !isCheckin);
if (files != null) {
int start = isCheckin ? 0 : (files.size() - maxNum);
if (start < 0) {
@@ -1142,7 +1223,7 @@
try {
AtomicFile file = new AtomicFile(new File(files.get(i)));
ProcessStats processStats = new ProcessStats(false);
- readLocked(processStats, file);
+ readLF(processStats, file);
if (processStats.mReadError != null) {
if (isCheckin || isCompact) pw.print("err,");
pw.print("Failure reading "); pw.print(files.get(i));
@@ -1188,11 +1269,11 @@
}
}
} finally {
- mWriteLock.unlock();
+ mFileLock.unlock();
}
}
if (!isCheckin) {
- synchronized (mAm) {
+ synchronized (mLock) {
if (isCompact) {
mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
} else {
@@ -1204,7 +1285,7 @@
mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
dumpAll, activeOnly, section);
if (dumpAll) {
- pw.print(" mFile="); pw.println(mFile.getBaseFile());
+ pw.print(" mFile="); pw.println(getCurrentFile());
}
} else {
mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1249,7 +1330,7 @@
// dump current procstats
long now;
- synchronized (mAm) {
+ synchronized (mLock) {
now = SystemClock.uptimeMillis();
final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
mProcessStats.dumpDebug(proto, now, ProcessStats.REPORT_ALL);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 022b04d..4a27030 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -528,8 +528,9 @@
return tracker;
}
if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
- tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
- serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode,
+ tracker = ams.mProcessStats.getServiceState(serviceInfo.packageName,
+ serviceInfo.applicationInfo.uid,
+ serviceInfo.applicationInfo.longVersionCode,
serviceInfo.processName, serviceInfo.name);
tracker.applyNewOwner(this);
}
@@ -546,7 +547,8 @@
public void makeRestarting(int memFactor, long now) {
if (restartTracker == null) {
if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
- restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
+ restartTracker = ams.mProcessStats.getServiceState(
+ serviceInfo.packageName,
serviceInfo.applicationInfo.uid,
serviceInfo.applicationInfo.longVersionCode,
serviceInfo.processName, serviceInfo.name);
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index e17cca4..cea5a69 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -88,7 +88,7 @@
// Default time limit in milliseconds for the ConnectivityManager to find a suitable
// network with SUPL connectivity or report an error.
- private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
+ private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000;
private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 249b6801..07527c2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -270,11 +270,12 @@
abstract boolean revertActiveSessions();
/**
- * Abandons the staged session with the given sessionId.
+ * Abandons the staged session with the given sessionId. Client should handle {@code false}
+ * return value carefully as failure here can leave device in inconsistent state.
*
- * @return {@code true} upon success, {@code false} if any remote exception occurs
+ * @return {@code true} upon success, {@code false} if any exception occurs
*/
- abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
+ abstract boolean abortStagedSession(int sessionId);
/**
* Uninstalls given {@code apexPackage}.
@@ -753,17 +754,13 @@
}
@Override
- boolean abortStagedSession(int sessionId) throws PackageManagerException {
+ boolean abortStagedSession(int sessionId) {
try {
waitForApexService().abortStagedSession(sessionId);
return true;
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to contact apexservice", re);
- return false;
} catch (Exception e) {
- throw new PackageManagerException(
- PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to abort staged session : " + e.getMessage());
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
}
}
@@ -1122,7 +1119,7 @@
}
@Override
- boolean abortStagedSession(int sessionId) throws PackageManagerException {
+ boolean abortStagedSession(int sessionId) {
throw new UnsupportedOperationException();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index aa2b125..05026a0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2091,7 +2091,7 @@
if (ps == null) {
return 0;
}
- final File apkDirOrPath = ps.codePath;
+ final File apkDirOrPath = ps.getCodePath();
if (apkDirOrPath == null) {
return 0;
}
@@ -3321,7 +3321,8 @@
/** {@hide} */
void setStagedSessionReady() {
synchronized (mLock) {
- if (mDestroyed) return; // Do not allow destroyed staged session to change state
+ // Do not allow destroyed/failed staged session to change state
+ if (mDestroyed || mStagedSessionFailed) return;
mStagedSessionReady = true;
mStagedSessionApplied = false;
mStagedSessionFailed = false;
@@ -3332,10 +3333,10 @@
}
/** {@hide} */
- void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
- String errorMessage) {
+ void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
synchronized (mLock) {
- if (mDestroyed) return; // Do not allow destroyed staged session to change state
+ // Do not allow destroyed/failed staged session to change state
+ if (mDestroyed || mStagedSessionFailed) return;
mStagedSessionReady = false;
mStagedSessionApplied = false;
mStagedSessionFailed = true;
@@ -3350,7 +3351,8 @@
/** {@hide} */
void setStagedSessionApplied() {
synchronized (mLock) {
- if (mDestroyed) return; // Do not allow destroyed staged session to change state
+ // Do not allow destroyed/failed staged session to change state
+ if (mDestroyed || mStagedSessionFailed) return;
mStagedSessionReady = false;
mStagedSessionApplied = true;
mStagedSessionFailed = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a726c8d..58a1648 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3010,7 +3010,7 @@
final int packageSettingCount = mSettings.mPackages.size();
for (int i = packageSettingCount - 1; i >= 0; i--) {
PackageSetting ps = mSettings.mPackages.valueAt(i);
- if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
+ if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists())
&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
mSettings.mPackages.removeAt(i);
mSettings.enableSystemPackageLPw(ps.name);
@@ -3175,11 +3175,11 @@
logCriticalInfo(Log.WARN,
"Expecting better updated system app for " + ps.name
+ "; removing system app. Last known"
- + " codePath=" + ps.codePathString
+ + " codePath=" + ps.getCodePathString()
+ ", versionCode=" + ps.versionCode
+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());
removePackageLI(scannedPkg, true);
- mExpectingBetter.put(ps.name, ps.codePath);
+ mExpectingBetter.put(ps.name, ps.getCodePath());
}
continue;
@@ -3202,14 +3202,14 @@
// code path, but, changes the package name.
final PackageSetting disabledPs =
mSettings.getDisabledSystemPkgLPr(ps.name);
- if (disabledPs.codePath == null || !disabledPs.codePath.exists()
+ if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists()
|| disabledPs.pkg == null) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
} else {
// We're expecting that the system app should remain disabled, but add
// it to expecting better to recover in case the data version cannot
// be scanned.
- mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
+ mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath());
}
}
}
@@ -8551,6 +8551,15 @@
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
+ if (listFactory) {
+ if (!ps.isSystem()) {
+ continue;
+ }
+ PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+ if (psDisabled != null) {
+ ps = psDisabled;
+ }
+ }
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
@@ -8565,7 +8574,16 @@
} else {
list = new ArrayList<>(mPackages.size());
for (AndroidPackage p : mPackages.values()) {
- final PackageSetting ps = getPackageSetting(p.getPackageName());
+ PackageSetting ps = getPackageSetting(p.getPackageName());
+ if (listFactory) {
+ if (!p.isSystem()) {
+ continue;
+ }
+ PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+ if (psDisabled != null) {
+ ps = psDisabled;
+ }
+ }
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
@@ -9150,7 +9168,7 @@
: getLastModifiedTime(parsedPackage);
final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
if (ps != null && !forceCollect
- && ps.codePathString.equals(parsedPackage.getCodePath())
+ && ps.getCodePathString().equals(parsedPackage.getCodePath())
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
@@ -9383,8 +9401,8 @@
}
}
- final boolean newPkgChangedPaths =
- pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath());
+ final boolean newPkgChangedPaths = pkgAlreadyExists
+ && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath());
final boolean newPkgVersionGreater =
pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
@@ -9403,11 +9421,11 @@
"System package updated;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath());
+ + "; " + pkgSetting.getCodePathString()
+ + " --> " + parsedPackage.getCodePath());
final InstallArgs args = createInstallArgsForExisting(
- pkgSetting.codePathString,
- pkgSetting.resourcePathString, getAppDexInstructionSets(
+ pkgSetting.getCodePathString(), getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
args.cleanUpResourcesLI();
synchronized (mLock) {
@@ -9482,11 +9500,10 @@
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> "
+ parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.codePathString + " --> "
+ + "; " + pkgSetting.getCodePathString() + " --> "
+ parsedPackage.getCodePath());
InstallArgs args = createInstallArgsForExisting(
- pkgSetting.codePathString,
- pkgSetting.resourcePathString, getAppDexInstructionSets(
+ pkgSetting.getCodePathString(), getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
@@ -9499,7 +9516,7 @@
logCriticalInfo(Log.INFO,
"System package disabled;"
+ " name: " + pkgSetting.name
- + "; old: " + pkgSetting.codePathString + " @ "
+ + "; old: " + pkgSetting.getCodePathString() + " @ "
+ pkgSetting.versionCode
+ "; new: " + parsedPackage.getCodePath() + " @ "
+ parsedPackage.getCodePath());
@@ -11325,7 +11342,7 @@
if (changedAbiCodePath == null) {
changedAbiCodePath = new ArrayList<>();
}
- changedAbiCodePath.add(ps.codePathString);
+ changedAbiCodePath.add(ps.getCodePathString());
}
}
}
@@ -11421,7 +11438,6 @@
// Initialize package source and resource directories
final File destCodeFile = new File(parsedPackage.getCodePath());
- final File destResourceFile = new File(parsedPackage.getCodePath());
// We keep references to the derived CPU Abis from settings in oder to reuse
// them in the case where we're not upgrading or booting for the first time.
@@ -11479,7 +11495,7 @@
// REMOVE SharedUserSetting from method; update in a separate call
pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
- destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
+ destCodeFile, parsedPackage.getNativeLibraryRootDir(),
AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
@@ -11497,7 +11513,7 @@
// secondaryCpuAbi are not known at this point so we always update them
// to null here, only to reset them at a later point.
Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
+ destCodeFile, parsedPackage.getNativeLibraryDir(),
AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
@@ -12066,15 +12082,13 @@
if (known != null) {
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Examining " + pkg.getCodePath()
- + " and requiring known paths " + known.codePathString
- + " & " + known.resourcePathString);
+ + " and requiring known path " + known.getCodePathString());
}
- if (!pkg.getCodePath().equals(known.codePathString)
- || !pkg.getCodePath().equals(known.resourcePathString)) {
+ if (!pkg.getCodePath().equals(known.getCodePathString())) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
"Application package " + pkg.getPackageName()
+ " found at " + pkg.getCodePath()
- + " but expected at " + known.codePathString
+ + " but expected at " + known.getCodePathString()
+ "; ignoring.");
}
} else {
@@ -15586,9 +15600,8 @@
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
*/
- private InstallArgs createInstallArgsForExisting(String codePath,
- String resourcePath, String[] instructionSets) {
- return new FileInstallArgs(codePath, resourcePath, instructionSets);
+ private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) {
+ return new FileInstallArgs(codePath, instructionSets);
}
static abstract class InstallArgs {
@@ -15669,10 +15682,8 @@
abstract boolean doRename(int status, ParsedPackage parsedPackage);
abstract int doPostInstall(int status, int uid);
- /** @see PackageSettingBase#codePathString */
+ /** @see PackageSettingBase#getCodePath() */
abstract String getCodePath();
- /** @see PackageSettingBase#resourcePathString */
- abstract String getResourcePath();
// Need installer lock especially for dex file removal.
abstract void cleanUpResourcesLI();
@@ -15743,14 +15754,13 @@
}
/** Existing install */
- FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
+ FileInstallArgs(String codePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, false,
DataLoaderType.NONE);
this.codeFile = (codePath != null) ? new File(codePath) : null;
- this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
int copyApk() {
@@ -15766,7 +15776,6 @@
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
- resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
@@ -15775,7 +15784,6 @@
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
- resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -15846,7 +15854,6 @@
// Reflect the rename internally
codeFile = afterCodeFile;
- resourceFile = afterCodeFile;
// Reflect the rename in scanned details
try {
@@ -15875,11 +15882,6 @@
return (codeFile != null) ? codeFile.getAbsolutePath() : null;
}
- @Override
- String getResourcePath() {
- return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
- }
-
private boolean cleanUp() {
if (codeFile == null || !codeFile.exists()) {
return false;
@@ -15892,10 +15894,6 @@
removeCodePathLI(codeFile);
- if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
- resourceFile.delete();
- }
-
return true;
}
@@ -15927,7 +15925,6 @@
*/
class MoveInstallArgs extends InstallArgs {
private File codeFile;
- private File resourceFile;
/** New install */
MoveInstallArgs(InstallParams params) {
@@ -15950,7 +15947,6 @@
final String toPathName = new File(move.fromCodePath).getName();
codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName);
- resourceFile = codeFile;
if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
return PackageManager.INSTALL_SUCCEEDED;
@@ -15987,11 +15983,6 @@
return (codeFile != null) ? codeFile.getAbsolutePath() : null;
}
- @Override
- String getResourcePath() {
- return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
- }
-
private boolean cleanUp(String volumeUuid) {
final String toPathName = new File(move.fromCodePath).getName();
final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
@@ -16777,7 +16768,6 @@
// installed. We need to make sure to delete the older one's .apk.
res.removedInfo.args = createInstallArgsForExisting(
oldPackage.getCodePath(),
- oldPackage.getCodePath(),
getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
deletedPkgSetting),
@@ -18558,7 +18548,6 @@
// user handle installed state
int[] allUsers;
/** enabled state of the uninstalled application */
- final int origEnabledState;
synchronized (mLock) {
uninstalledPs = mSettings.mPackages.get(packageName);
if (uninstalledPs == null) {
@@ -18574,10 +18563,6 @@
}
disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
- // Save the enabled state before we delete the package. When deleting a stub
- // application we always set the enabled state to 'disabled'.
- origEnabledState = uninstalledPs == null
- ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
// Static shared libs can be declared by any package, so let us not
// allow removing a package if it provides a lib others depend on.
pkg = mPackages.get(packageName);
@@ -18656,20 +18641,32 @@
if (stubPkg != null && stubPkg.isStub()) {
final PackageSetting stubPs;
synchronized (mLock) {
- // restore the enabled state of the stub; the state is overwritten when
- // the stub is uninstalled
stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
- if (stubPs != null) {
- stubPs.setEnabled(origEnabledState, userId, "android");
- }
}
- if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
- || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
- if (DEBUG_COMPRESSION) {
- Slog.i(TAG, "Enabling system stub after removal; pkg: "
- + stubPkg.getPackageName());
+
+ if (stubPs != null) {
+ boolean enable = false;
+ for (int aUserId : allUsers) {
+ if (stubPs.getInstalled(aUserId)) {
+ int enabled = stubPs.getEnabled(aUserId);
+ if (enabled == COMPONENT_ENABLED_STATE_DEFAULT
+ || enabled == COMPONENT_ENABLED_STATE_ENABLED) {
+ enable = true;
+ break;
+ }
+ }
}
- enableCompressedPackage(stubPkg, stubPs);
+
+ if (enable) {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ + stubPkg.getPackageName());
+ }
+ enableCompressedPackage(stubPkg, stubPs);
+ } else if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+ + "after removal; pkg: " + stubPkg.getPackageName());
+ }
}
}
}
@@ -18998,7 +18995,7 @@
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
- installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
+ installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles,
outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
writeSettings);
} catch (PackageManagerException e) {
@@ -19013,8 +19010,15 @@
// and re-enable it afterward.
final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
if (stubPs != null) {
- stubPs.setEnabled(
- COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
+ int userId = action.user == null
+ ? UserHandle.USER_ALL : action.user.getIdentifier();
+ if (userId == UserHandle.USER_ALL) {
+ for (int aUserId : allUserHandles) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+ }
+ } else if (userId >= UserHandle.USER_SYSTEM) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+ }
}
}
}
@@ -19123,7 +19127,7 @@
// Delete application code and resources only for parent packages
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgsForExisting(
- ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(
+ ps.getCodePathString(), getAppDexInstructionSets(
ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
@@ -19660,7 +19664,7 @@
final String[] packageNames = { packageName };
final long[] ceDataInodes = { ps.getCeDataInode(userId) };
- final String[] codePaths = { ps.codePathString };
+ final String[] codePaths = { ps.getCodePathString() };
try {
mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
@@ -22582,11 +22586,11 @@
synchronized (mInstallLock) {
final AndroidPackage pkg;
try {
- pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
+ pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg);
} catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
+ Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage());
}
if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
@@ -22657,33 +22661,33 @@
final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
synchronized (mInstallLock) {
- synchronized (mLock) {
- final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
- for (PackageSetting ps : packages) {
- if (ps.pkg == null) continue;
+ synchronized (mLock) {
+ final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
+ for (PackageSetting ps : packages) {
+ if (ps.pkg == null) continue;
- final AndroidPackage pkg = ps.pkg;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
+ final AndroidPackage pkg = ps.pkg;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
+ final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
- try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
- "unloadPrivatePackagesInner")) {
- if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
- false, null)) {
- unloaded.add(pkg);
- } else {
- Slog.w(TAG, "Failed to unload " + ps.codePath);
+ try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
+ "unloadPrivatePackagesInner")) {
+ if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
+ false, null)) {
+ unloaded.add(pkg);
+ } else {
+ Slog.w(TAG, "Failed to unload " + ps.getCodePath());
+ }
}
+
+ // Try very hard to release any references to this package
+ // so we don't risk the system server being killed due to
+ // open FDs
+ AttributeCache.instance().removePackage(ps.name);
}
- // Try very hard to release any references to this package
- // so we don't risk the system server being killed due to
- // open FDs
- AttributeCache.instance().removePackage(ps.name);
+ mSettings.writeLPr();
}
-
- mSettings.writeLPr();
- }
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
@@ -22726,7 +22730,7 @@
final int packageCount = mSettings.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
- codePaths.add(ps.codePath.getAbsolutePath());
+ codePaths.add(ps.getCodePath().getAbsolutePath());
}
return codePaths;
}
@@ -23643,8 +23647,20 @@
}
}
- void onNewUserCreated(final int userId) {
- mPermissionManager.onNewUserCreated(userId);
+ void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.d(TAG, "onNewUserCreated(id=" + userId
+ + ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
+ }
+ if (!convertedFromPreCreated) {
+ mPermissionManager.onNewUserCreated(userId);
+ return;
+ }
+ if (!readPermissionStateForUser(userId)) {
+ // Could not read the existing permissions, re-grant them.
+ Slog.i(TAG, "re-granting permissions for pre-created user " + userId);
+ mPermissionManager.onNewUserCreated(userId);
+ }
}
boolean readPermissionStateForUser(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 668f375..7aeec6d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -423,13 +423,15 @@
final List<ApplicationInfo> list;
if (packageName == null) {
final ParceledListSlice<ApplicationInfo> packages =
- mInterface.getInstalledApplications(
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ UserHandle.USER_SYSTEM);
list = packages.getList();
} else {
list = new ArrayList<>(1);
- list.add(mInterface.getApplicationInfo(packageName,
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
+ list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ UserHandle.USER_SYSTEM));
}
for (ApplicationInfo info : list) {
if (info.isUpdatedSystemApp()) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 432d7f3..a3a7273 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -71,13 +71,13 @@
private PackageStateUnserialized pkgState = new PackageStateUnserialized();
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public PackageSetting(String name, String realName, File codePath, File resourcePath,
+ public PackageSetting(String name, String realName, @NonNull File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
long pVersionCode, int pkgFlags, int privateFlags,
int sharedUserId, String[] usesStaticLibraries,
long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) {
- super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
+ super(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
pVersionCode, pkgFlags, privateFlags,
usesStaticLibraries, usesStaticLibrariesVersions);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 834303c..6010344 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -64,10 +64,8 @@
* this is path to single base APK file; for cluster packages this is
* path to the cluster directory.
*/
- File codePath;
- String codePathString;
- File resourcePath;
- String resourcePathString;
+ private File mCodePath;
+ private String mCodePathString;
String[] usesStaticLibraries;
long[] usesStaticLibrariesVersions;
@@ -138,7 +136,7 @@
boolean forceQueryableOverride;
- PackageSettingBase(String name, String realName, File codePath, File resourcePath,
+ PackageSettingBase(String name, String realName, @NonNull File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
long pVersionCode, int pkgFlags, int pkgPrivateFlags,
@@ -148,10 +146,7 @@
this.realName = realName;
this.usesStaticLibraries = usesStaticLibraries;
this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
- this.codePath = codePath;
- this.codePathString = codePath.toString();
- this.resourcePath = resourcePath;
- this.resourcePathString = resourcePath.toString();
+ setCodePath(codePath);
this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
this.primaryCpuAbiString = primaryCpuAbiString;
this.secondaryCpuAbiString = secondaryCpuAbiString;
@@ -235,8 +230,7 @@
}
private void doCopy(PackageSettingBase orig) {
- codePath = orig.codePath;
- codePathString = orig.codePathString;
+ setCodePath(orig.getCodePath());
cpuAbiOverrideString = orig.cpuAbiOverrideString;
firstInstallTime = orig.firstInstallTime;
installPermissionsFixed = orig.installPermissionsFixed;
@@ -246,8 +240,6 @@
legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
// Intentionally skip mOldCodePaths; it's not relevant for copies
primaryCpuAbiString = orig.primaryCpuAbiString;
- resourcePath = orig.resourcePath;
- resourcePathString = orig.resourcePathString;
secondaryCpuAbiString = orig.secondaryCpuAbiString;
signatures = orig.signatures;
timeStamp = orig.timeStamp;
@@ -705,6 +697,20 @@
return userState.harmfulAppWarning;
}
+ PackageSettingBase setCodePath(@NonNull File codePath) {
+ this.mCodePath = codePath;
+ this.mCodePathString = codePath.toString();
+ return this;
+ }
+
+ File getCodePath() {
+ return mCodePath;
+ }
+
+ String getCodePathString() {
+ return mCodePathString;
+ }
+
/**
* @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
*
@@ -727,10 +733,7 @@
protected PackageSettingBase updateFrom(PackageSettingBase other) {
super.copyFrom(other);
- this.codePath = other.codePath;
- this.codePathString = other.codePathString;
- this.resourcePath = other.resourcePath;
- this.resourcePathString = other.resourcePathString;
+ setCodePath(other.getCodePath());
this.usesStaticLibraries = other.usesStaticLibraries;
this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1805713..3e3e3c5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -538,7 +538,7 @@
return null;
}
p.getPkgState().setUpdatedSystemApp(false);
- PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
+ PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(),
p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
p.secondaryCpuAbiString, p.cpuAbiOverrideString,
p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
@@ -558,7 +558,7 @@
mDisabledSysPackages.remove(name);
}
- PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
+ PackageSetting addPackageLPw(String name, String realName, File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
@@ -572,10 +572,9 @@
"Adding duplicate package, keeping first: " + name);
return null;
}
- p = new PackageSetting(name, realName, codePath, resourcePath,
- legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags,
- 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
+ p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
+ primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
+ pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
mimeGroups);
p.appId = uid;
if (registerExistingAppIdLPw(uid, p, name)) {
@@ -635,7 +634,7 @@
*/
static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
- File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
+ File codePath, String legacyNativeLibraryPath, String primaryCpuAbi,
String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
UserHandle installUser, boolean allowInstall, boolean instantApp,
boolean virtualPreload, UserManagerService userManager,
@@ -646,12 +645,11 @@
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
+ pkgName + " is adopting original package " + originalPkg.name);
pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
- pkgSetting.codePath = codePath;
+ pkgSetting.setCodePath(codePath);
pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
pkgSetting.pkgFlags = pkgFlags;
pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
pkgSetting.primaryCpuAbiString = primaryCpuAbi;
- pkgSetting.resourcePath = resourcePath;
pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
// NOTE: Create a deeper copy of the package signatures so we don't
// overwrite the signatures in the original package setting.
@@ -662,7 +660,7 @@
// Update new package state.
pkgSetting.setTimeStamp(codePath.lastModified());
} else {
- pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
+ pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
0 /*sharedUserId*/, usesStaticLibraries,
@@ -756,10 +754,9 @@
*/
static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
@Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
- @NonNull File codePath, File resourcePath,
- @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi,
- @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags,
- @NonNull UserManagerService userManager,
+ @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
+ @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
+ int pkgPrivateFlags, @NonNull UserManagerService userManager,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
@Nullable Set<String> mimeGroupNames)
throws PackageManagerException {
@@ -773,12 +770,12 @@
"Updating application package " + pkgName + " failed");
}
- if (!pkgSetting.codePath.equals(codePath)) {
+ if (!pkgSetting.getCodePath().equals(codePath)) {
final boolean isSystem = pkgSetting.isSystem();
Slog.i(PackageManagerService.TAG,
"Update" + (isSystem ? " system" : "")
+ " package " + pkgName
- + " code path from " + pkgSetting.codePathString
+ + " code path from " + pkgSetting.getCodePathString()
+ " to " + codePath.toString()
+ "; Retain data and using new");
if (!isSystem) {
@@ -800,19 +797,7 @@
// internal to external storage or vice versa.
pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
}
- pkgSetting.codePath = codePath;
- pkgSetting.codePathString = codePath.toString();
- }
- if (!pkgSetting.resourcePath.equals(resourcePath)) {
- final boolean isSystem = pkgSetting.isSystem();
- Slog.i(PackageManagerService.TAG,
- "Update" + (isSystem ? " system" : "")
- + " package " + pkgName
- + " resource path from " + pkgSetting.resourcePathString
- + " to " + resourcePath.toString()
- + "; Retain data and using new");
- pkgSetting.resourcePath = resourcePath;
- pkgSetting.resourcePathString = resourcePath.toString();
+ pkgSetting.setCodePath(codePath);
}
// If what we are scanning is a system (and possibly privileged) package,
// then make it so, regardless of whether it was previously installed only
@@ -2710,7 +2695,7 @@
private void writePackageListLPrInternal(int creatingUserId) {
// Only derive GIDs for active users (not dying)
- final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true);
+ final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true);
int[] userIds = new int[users.size()];
for (int i = 0; i < userIds.length; i++) {
userIds[i] = users.get(i).id;
@@ -2812,14 +2797,11 @@
if (pkg.realName != null) {
serializer.attribute(null, "realName", pkg.realName);
}
- serializer.attribute(null, "codePath", pkg.codePathString);
+ serializer.attribute(null, "codePath", pkg.getCodePathString());
serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
- if (!pkg.resourcePathString.equals(pkg.codePathString)) {
- serializer.attribute(null, "resourcePath", pkg.resourcePathString);
- }
if (pkg.legacyNativeLibraryPathString != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
}
@@ -2857,10 +2839,7 @@
if (pkg.realName != null) {
serializer.attribute(null, "realName", pkg.realName);
}
- serializer.attribute(null, "codePath", pkg.codePathString);
- if (!pkg.resourcePathString.equals(pkg.codePathString)) {
- serializer.attribute(null, "resourcePath", pkg.resourcePathString);
- }
+ serializer.attribute(null, "codePath", pkg.getCodePathString());
if (pkg.legacyNativeLibraryPathString != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
@@ -3559,13 +3538,10 @@
String name = parser.getAttributeValue(null, ATTR_NAME);
String realName = parser.getAttributeValue(null, "realName");
String codePathStr = parser.getAttributeValue(null, "codePath");
- String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi");
String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
- String parentPackageName = parser.getAttributeValue(null, "parentPackageName");
-
String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi");
String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi");
String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride");
@@ -3574,9 +3550,6 @@
primaryCpuAbiStr = legacyCpuAbiStr;
}
- if (resourcePathStr == null) {
- resourcePathStr = codePathStr;
- }
String version = parser.getAttributeValue(null, "version");
long versionCode = 0;
if (version != null) {
@@ -3593,9 +3566,8 @@
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
- new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
- secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
- 0 /*sharedUserId*/, null, null, null);
+ legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
+ versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null);
String timeStampStr = parser.getAttributeValue(null, "ft");
if (timeStampStr != null) {
try {
@@ -3666,7 +3638,6 @@
String idStr = null;
String sharedIdStr = null;
String codePathStr = null;
- String resourcePathStr = null;
String legacyCpuAbiString = null;
String legacyNativeLibraryPathStr = null;
String primaryCpuAbiString = null;
@@ -3700,7 +3671,6 @@
uidError = parser.getAttributeValue(null, "uidError");
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
codePathStr = parser.getAttributeValue(null, "codePath");
- resourcePathStr = parser.getAttributeValue(null, "resourcePath");
legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
@@ -3818,9 +3788,6 @@
+ " sharedUserId=" + sharedIdStr);
final int userId = idStr != null ? Integer.parseInt(idStr) : 0;
final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
- if (resourcePathStr == null) {
- resourcePathStr = codePathStr;
- }
if (realName != null) {
realName = realName.intern();
}
@@ -3834,10 +3801,10 @@
+ parser.getPositionDescription());
} else if (userId > 0) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
- new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
- secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
- pkgPrivateFlags, null /*usesStaticLibraries*/,
- null /*usesStaticLibraryVersions*/, null /*mimeGroups*/);
+ legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
+ cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
+ null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
+ null /*mimeGroups*/);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
@@ -3852,8 +3819,8 @@
}
} else if (sharedIdStr != null) {
if (sharedUserId > 0) {
- packageSetting = new PackageSetting(name.intern(), realName, new File(
- codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
+ packageSetting = new PackageSetting(name.intern(), realName,
+ new File(codePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
null /*usesStaticLibraries*/,
@@ -4456,25 +4423,43 @@
}
/**
- * Return all users on the device, including partial or dying users.
+ * Returns all users on the device, including pre-created and dying users.
+ *
* @param userManager UserManagerService instance
* @return the list of users
*/
private static List<UserInfo> getAllUsers(UserManagerService userManager) {
- return getUsers(userManager, false);
+ return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false);
}
/**
- * Return the list of users on the device. Clear the calling identity before calling into
- * UserManagerService.
+ * Returns the list of users on the device, excluding pre-created ones.
+ *
* @param userManager UserManagerService instance
* @param excludeDying Indicates whether to exclude any users marked for deletion.
+ *
* @return the list of users
*/
- private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) {
+ private static List<UserInfo> getActiveUsers(UserManagerService userManager,
+ boolean excludeDying) {
+ return getUsers(userManager, excludeDying, /* excludePreCreated= */ true);
+ }
+
+ /**
+ * Returns the list of users on the device.
+ *
+ * @param userManager UserManagerService instance
+ * @param excludeDying Indicates whether to exclude any users marked for deletion.
+ * @param excludePreCreated Indicates whether to exclude any pre-created users.
+ *
+ * @return the list of users
+ */
+ private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying,
+ boolean excludePreCreated) {
long id = Binder.clearCallingIdentity();
try {
- return userManager.getUsers(excludeDying);
+ return userManager.getUsers(/* excludePartial= */ true, excludeDying,
+ excludePreCreated);
} catch (NullPointerException npe) {
// packagemanager not yet initialized
} finally {
@@ -4657,9 +4642,9 @@
pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser);
}
pw.print(prefix); pw.print(" pkg="); pw.println(pkg);
- pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString);
+ pw.print(prefix); pw.print(" codePath="); pw.println(ps.getCodePathString());
if (permissionNames == null) {
- pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString);
+ pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getCodePathString());
pw.print(prefix); pw.print(" legacyNativeLibraryDir=");
pw.println(ps.legacyNativeLibraryPathString);
pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 89bdb3e..f9bf54a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -322,9 +322,6 @@
}
final long activeVersion = activePackage.applicationInfo.longVersionCode;
if (activeVersion != session.params.requiredInstalledVersionCode) {
- if (!mApexManager.abortStagedSession(session.sessionId)) {
- Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
- }
throw new PackageManagerException(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Installed version of APEX package " + activePackage.packageName
@@ -338,14 +335,11 @@
throws PackageManagerException {
final long activeVersion = activePackage.applicationInfo.longVersionCode;
final long newVersionCode = newPackage.applicationInfo.longVersionCode;
- boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
& ApplicationInfo.FLAG_DEBUGGABLE) != 0;
final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
session.params.installFlags, isAppDebuggable);
if (activeVersion > newVersionCode && !allowsDowngrade) {
- if (!mApexManager.abortStagedSession(session.sessionId)) {
- Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
- }
throw new PackageManagerException(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Downgrade of APEX package " + newPackage.packageName
@@ -835,37 +829,6 @@
return null;
}
- private void verifyApksInSession(PackageInstallerSession session)
- throws PackageManagerException {
-
- final PackageInstallerSession apksToVerify = extractApksInSession(
- session, /* preReboot */ true);
- if (apksToVerify == null) {
- return;
- }
-
- final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
- (Intent result) -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to verify APK staged session "
- + session.sessionId + " [" + errorMessage + "]");
- session.setStagedSessionFailed(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
- mPreRebootVerificationHandler.onPreRebootVerificationComplete(
- session.sessionId);
- return;
- }
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
- session.sessionId);
- });
-
- apksToVerify.commit(receiver.getIntentSender(), false);
- }
-
private void installApksInSession(@NonNull PackageInstallerSession session)
throws PackageManagerException {
@@ -908,10 +871,21 @@
mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
}
- private int parentOrOwnSessionId(PackageInstallerSession session) {
+ private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
}
+ private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
+ return session.hasParentSessionId()
+ ? getStagedSession(session.getParentSessionId())
+ : session;
+ }
+
+ private boolean isRollback(PackageInstallerSession session) {
+ final PackageInstallerSession root = getParentSessionOrSelf(session);
+ return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ }
+
/**
* <p> Check if the session provided is non-overlapping with the active staged sessions.
*
@@ -937,6 +911,8 @@
boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
Context.STORAGE_SERVICE)).isCheckpointSupported();
+ final boolean isRollback = isRollback(session);
+
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -951,8 +927,8 @@
}
// Check if stagedSession has an active parent session or not
if (stagedSession.hasParentSessionId()) {
- int parentId = stagedSession.getParentSessionId();
- PackageInstallerSession parentSession = mStagedSessions.get(parentId);
+ final int parentId = stagedSession.getParentSessionId();
+ final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
if (parentSession == null || parentSession.isStagedAndInTerminalState()
|| parentSession.isDestroyed()) {
// Parent session has been abandoned or terminated already
@@ -968,21 +944,37 @@
continue;
}
- // If session is not among the active sessions, then it cannot have same package
- // name as any of the active sessions.
+ // New session cannot have same package name as one of the active sessions
if (session.getPackageName().equals(stagedSession.getPackageName())) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
- "Package: " + session.getPackageName() + " in session: "
- + session.sessionId + " has been staged already by session: "
- + stagedSession.sessionId, null);
+ if (isRollback) {
+ // If the new session is a rollback, then it gets priority. The existing
+ // session is failed to unblock rollback.
+ final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+ if (!ensureActiveApexSessionIsAborted(root)) {
+ Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
+ // Safe to ignore active apex session abort failure since session
+ // will be marked failed on next step and staging directory for session
+ // will be deleted.
+ }
+ root.setStagedSessionFailed(
+ SessionInfo.STAGED_SESSION_OTHER_ERROR,
+ "Session was blocking rollback session: " + session.sessionId);
+ Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
+ + "blocking rollback session: " + session.sessionId);
+ } else {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+ "Package: " + session.getPackageName() + " in session: "
+ + session.sessionId + " has been staged already by session:"
+ + " " + stagedSession.sessionId, null);
+ }
}
// Staging multiple root sessions is not allowed if device doesn't support
// checkpoint. If session and stagedSession do not have common ancestor, they are
// from two different root sessions.
- if (!supportsCheckpoint
- && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+ if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
+ != getSessionIdForParentOrSelf(stagedSession)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Cannot stage multiple sessions without checkpoint support", null);
@@ -1042,23 +1034,11 @@
// A session could be marked ready once its pre-reboot verification ends
if (session.isStagedSessionReady()) {
- if (sessionContainsApex(session)) {
- try {
- ApexSessionInfo apexSession =
- mApexManager.getStagedSessionInfo(session.sessionId);
- if (apexSession == null || isApexSessionFinalized(apexSession)) {
- Slog.w(TAG,
- "Cannot abort session " + session.sessionId
- + " because it is not active.");
- } else {
- mApexManager.abortStagedSession(session.sessionId);
- }
- } catch (Exception e) {
- // Failed to contact apexd service. The apex might still be staged. We can still
- // safely cleanup the staged session since pre-reboot verification is complete.
- // Also, cleaning up the stageDir prevents the apex from being activated.
- Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
- }
+ if (!ensureActiveApexSessionIsAborted(session)) {
+ // Failed to ensure apex session is aborted, so it can still be staged. We can still
+ // safely cleanup the staged session since pre-reboot verification is complete.
+ // Also, cleaning up the stageDir prevents the apex from being activated.
+ Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
}
}
@@ -1068,6 +1048,22 @@
return true;
}
+ /**
+ * Ensure that there is no active apex session staged in apexd for the given session.
+ *
+ * @return returns true if it is ensured that there is no active apex session, otherwise false
+ */
+ private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
+ if (!sessionContainsApex(session)) {
+ return true;
+ }
+ final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
+ if (apexSession == null || isApexSessionFinalized(apexSession)) {
+ return true;
+ }
+ return mApexManager.abortStagedSession(session.sessionId);
+ }
+
private boolean isApexSessionFinalized(ApexSessionInfo session) {
/* checking if the session is in a final state, i.e., not active anymore */
return session.isUnknown || session.isActivationFailed || session.isSuccess
@@ -1294,8 +1290,8 @@
+ sessionId);
return;
}
- if (session.isDestroyed()) {
- // No point in running verification on a destroyed session
+ if (session.isDestroyed() || session.isStagedSessionFailed()) {
+ // No point in running verification on a destroyed/failed session
onPreRebootVerificationComplete(sessionId);
return;
}
@@ -1348,6 +1344,17 @@
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
}
+ private void onPreRebootVerificationFailure(PackageInstallerSession session,
+ @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+ if (!ensureActiveApexSessionIsAborted(session)) {
+ Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
+ // Safe to ignore active apex session abortion failure since session will be marked
+ // failed on next step and staging directory for session will be deleted.
+ }
+ session.setStagedSessionFailed(errorCode, errorMessage);
+ onPreRebootVerificationComplete(session.sessionId);
+ }
+
// Things to do when pre-reboot verification completes for a particular sessionId
private void onPreRebootVerificationComplete(int sessionId) {
// Remove it from mVerificationRunning so that verification is considered complete
@@ -1432,8 +1439,7 @@
validateApexSignature(apexPackages.get(i));
}
} catch (PackageManagerException e) {
- session.setStagedSessionFailed(e.error, e.getMessage());
- onPreRebootVerificationComplete(session.sessionId);
+ onPreRebootVerificationFailure(session, e.error, e.getMessage());
return;
}
@@ -1460,16 +1466,42 @@
try {
Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+ session.sessionId + " by performing a dry-run install");
-
// verifyApksInSession will notify the handler when APK verification is complete
verifyApksInSession(session);
- // TODO(b/118865310): abort the session on apexd.
} catch (PackageManagerException e) {
- session.setStagedSessionFailed(e.error, e.getMessage());
- onPreRebootVerificationComplete(session.sessionId);
+ onPreRebootVerificationFailure(session, e.error, e.getMessage());
}
}
+ private void verifyApksInSession(PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToVerify = extractApksInSession(
+ session, /* preReboot */ true);
+ if (apksToVerify == null) {
+ return;
+ }
+
+ final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+ (Intent result) -> {
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to verify APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ onPreRebootVerificationFailure(session,
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+ return;
+ }
+ mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+ session.sessionId);
+ });
+
+ apksToVerify.commit(receiver.getIntentSender(), false);
+ }
+
/**
* Pre-reboot verification state for wrapping up:
* <p><ul>
@@ -1487,9 +1519,8 @@
} catch (Exception e) {
// Failed to get hold of StorageManager
Slog.e(TAG, "Failed to get hold of StorageManager", e);
- session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+ onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN,
"Failed to get hold of StorageManager");
- onPreRebootVerificationComplete(session.sessionId);
return;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8f11fd5..e3bee72 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3497,7 +3497,7 @@
}
t.traceBegin("PM.onNewUserCreated-" + userId);
- mPm.onNewUserCreated(userId);
+ mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
t.traceEnd();
if (preCreate) {
// Must start user (which will be stopped right away, through
@@ -3570,10 +3570,7 @@
writeUserListLP();
}
updateUserIds();
- if (!mPm.readPermissionStateForUser(preCreatedUser.id)) {
- // Could not read the existing permissions, re-grant them.
- mPm.onNewUserCreated(preCreatedUser.id);
- }
+ mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
dispatchUserAdded(preCreatedUser);
return preCreatedUser;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a05289f..2c475e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -130,6 +130,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.Display;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -1090,9 +1091,9 @@
// Check if caller is already present on display
final boolean uidPresentOnDisplay = displayContent.isUidPresent(callingUid);
- final int displayOwnerUid = displayContent.mDisplay.getOwnerUid();
- if (displayContent.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID) {
- // Limit launching on virtual displays, because their contents can be read from Surface
+ final Display display = displayContent.mDisplay;
+ if (!display.isTrusted()) {
+ // Limit launching on untrusted displays because their contents can be read from Surface
// by apps that created them.
if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -1116,7 +1117,7 @@
}
// Check if the caller is the owner of the display.
- if (displayOwnerUid == callingUid) {
+ if (display.getOwnerUid() == callingUid) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ " allow launch for owner of the display");
return true;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f47ddef..be0815b0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2365,7 +2365,6 @@
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
if (mWmService.mDisableTransitionAnimation
|| !isVisible()
- || getDisplayContent().mAppTransition.isTransitionSet()
|| getSurfaceControl() == null
|| !isLeafTask()) {
return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index f3c7a5d..9b18ac8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
@@ -142,6 +143,7 @@
private final Handler mHandler;
private boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
+ private final int mActivityType;
private final int mStatusBarColor;
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private final int mOrientationOnCreation;
@@ -173,6 +175,7 @@
final int windowFlags;
final int windowPrivateFlags;
final int currentOrientation;
+ final int activityType;
final InsetsState insetsState;
synchronized (service.mGlobalLock) {
final WindowState mainWindow = activity.findMainWindow();
@@ -241,6 +244,7 @@
taskBounds = new Rect();
task.getBounds(taskBounds);
currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
+ activityType = activity.getActivityType();
final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
.getInsetsPolicy();
@@ -261,7 +265,8 @@
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
- windowFlags, windowPrivateFlags, taskBounds, currentOrientation, insetsState);
+ windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType,
+ insetsState);
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
@@ -282,7 +287,7 @@
TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
- int currentOrientation, InsetsState insetsState) {
+ int currentOrientation, int activityType, InsetsState insetsState) {
mService = service;
mSurface = service.mSurfaceFactory.get();
mHandler = new Handler(mService.mH.getLooper());
@@ -298,6 +303,7 @@
windowPrivateFlags, sysUiVis, taskDescription, 1f, insetsState);
mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
+ mActivityType = activityType;
mTransaction = mService.mTransactionFactory.get();
}
@@ -305,7 +311,9 @@
public void remove() {
synchronized (mService.mGlobalLock) {
final long now = SystemClock.uptimeMillis();
- if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
+ if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
+ // Show the latest content as soon as possible for unlocking to home.
+ && mActivityType != ACTIVITY_TYPE_HOME) {
mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"Defer removing snapshot surface in %dms", (now - mShownTime));
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index ecdb30f..09552082 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -33,7 +33,6 @@
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
-import com.android.server.pm.test.override.R
import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
import com.android.server.testutils.mockThrowOnUnmocked
@@ -266,7 +265,7 @@
.hideAsFinal()
private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"),
- File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) {
+ null, null, null, null, 0, 0, 0, 0, null, null, null)) {
this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
}
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 41dfade..cffcdd8 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -25,17 +25,28 @@
],
test_suites: ["general-tests"],
java_resources: [
- ":PackageManagerDummyAppVersion1",
- ":PackageManagerDummyAppVersion2",
- ":PackageManagerDummyAppVersion3",
- ":PackageManagerDummyAppVersion4",
- ":PackageManagerDummyAppOriginalOverride",
- ":PackageManagerServiceHostTestsResources",
- ]
+ ":PackageManagerTestAppStub",
+ ":PackageManagerTestAppVersion1",
+ ":PackageManagerTestAppVersion2",
+ ":PackageManagerTestAppVersion3",
+ ":PackageManagerTestAppVersion3Invalid",
+ ":PackageManagerTestAppVersion4",
+ ":PackageManagerTestAppOriginalOverride",
+ ],
}
-filegroup {
- name: "PackageManagerServiceHostTestsResources",
- srcs: [ "resources/*" ],
- path: "resources/"
+genrule {
+ name: "PackageManagerTestAppVersion3Invalid",
+ tools: [
+ "soong_zip",
+ "zipalign",
+ ],
+ srcs: [
+ ":PackageManagerTestAppVersion3",
+ ],
+ out: ["PackageManagerTestAppVersion3Invalid.apk"],
+ cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" +
+ " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" +
+ " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
+ " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
}
diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
deleted file mode 100644
index 127886c..0000000
--- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
+++ /dev/null
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 234fcf1..8dfefaf 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -21,8 +21,9 @@
import java.io.File
import java.io.FileOutputStream
-internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
- pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString())
+internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
+ pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
+ .toString())
internal fun SystemPreparer.deleteApkFolders(
partition: Partition,
@@ -58,4 +59,55 @@
}
return file
}
+
+ /**
+ * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot,
+ * so the following methods parse the package dump directly to see if the path matches.
+ */
+ fun getCodePaths(device: ITestDevice, pkgName: String) =
+ device.executeShellCommand("pm dump $pkgName")
+ .lineSequence()
+ .map(String::trim)
+ .filter { it.startsWith("codePath=") }
+ .map { it.removePrefix("codePath=") }
+ .toList()
+
+ private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
+ device.executeShellCommand("pm dump $pkgName")
+ .lineSequence()
+ .dropWhile { !it.startsWith("Packages:") }
+ .takeWhile {
+ !it.startsWith("Hidden system packages:") &&
+ !it.startsWith("Queries:")
+ }
+ .map(String::trim)
+ .filter { it.startsWith("User ") }
+
+ fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) =
+ userIdLineSequence(device, pkgName).associate {
+ val userId = it.removePrefix("User ")
+ .takeWhile(Char::isDigit)
+ .toInt()
+ val enabled = it.substringAfter("enabled=")
+ .takeWhile(Char::isDigit)
+ .toInt()
+ .let {
+ when (it) {
+ 0, 1 -> true
+ else -> false
+ }
+ }
+ userId to enabled
+ }
+
+ fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) =
+ userIdLineSequence(device, pkgName).associate {
+ val userId = it.removePrefix("User ")
+ .takeWhile(Char::isDigit)
+ .toInt()
+ val installed = it.substringAfter("installed=")
+ .takeWhile { !it.isWhitespace() }
+ .toBoolean()
+ userId to installed
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index 39b40d8..b7d1359 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -33,11 +33,11 @@
class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
companion object {
- private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
- private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
- private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
- private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk"
- private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk"
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+ private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+ private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
+ private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
@get:ClassRule
val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -49,14 +49,14 @@
@get:Rule
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
- private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT)
+ private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT)
@Before
@After
fun removeApk() {
device.uninstallPackage(TEST_PKG_NAME)
- device.deleteFile(filePath.parent.toString())
- device.reboot()
+ preparer.deleteFile(filePath.parent.toString())
+ .reboot()
}
@Test
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index fb0348c..4ae3ca5 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -33,11 +33,11 @@
class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
companion object {
- private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
- private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
- private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
- private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk"
- private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk"
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+ private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+ private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
+ private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk"
@get:ClassRule
val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -55,6 +55,7 @@
fun deleteApkFolders() {
preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
NEW_PKG)
+ .reboot()
}
@Test
@@ -99,9 +100,7 @@
}
private fun assertCodePath(apk: String) {
- // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason,
- // so parse the package dump directly to see if the path matches.
- assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME"))
- .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
+ assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME))
+ .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
}
}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
new file mode 100644
index 0000000..bc478b0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -0,0 +1,650 @@
+/*
+ * 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.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.device.ITestDevice
+import com.android.tradefed.device.UserInfo
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.AfterClass
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+import java.util.zip.GZIPOutputStream
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
+
+ companion object {
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val VERSION_STUB = "PackageManagerTestAppStub.apk"
+ private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+
+ /**
+ * How many total users on device to test, including primary. This will clean up any
+ * users created specifically for this test.
+ */
+ private const val USER_COUNT = 3
+
+ /**
+ * Whether to manually reset state at each test method without rebooting
+ * for faster iterative development.
+ */
+ private const val DEBUG_NO_REBOOT = false
+
+ @get:ClassRule
+ val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+
+ private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName
+
+ private val deviceCompressedFile =
+ HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent
+ .resolve("$parentClassName.apk.gz")
+
+ private val stubFile =
+ HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT)
+
+ private val secondaryUsers = mutableListOf<Int>()
+ private val usersToRemove = mutableListOf<Int>()
+ private var savedDevice: ITestDevice? = null
+ private var savedPreparer: SystemPreparer? = null
+
+ private fun setUpUsers(device: ITestDevice) {
+ if (this.savedDevice != null) return
+ this.savedDevice = device
+ secondaryUsers.clear()
+ secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 }
+ while (secondaryUsers.size < USER_COUNT) {
+ secondaryUsers += device.createUser(parentClassName + secondaryUsers.size)
+ .also { usersToRemove += it }
+ }
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun cleanUp() {
+ savedDevice ?: return
+
+ usersToRemove.forEach {
+ savedDevice?.removeUser(it)
+ }
+
+ savedDevice?.uninstallPackage(TEST_PKG_NAME)
+ savedDevice?.deleteFile(stubFile.parent.toString())
+ savedDevice?.deleteFile(deviceCompressedFile.parent.toString())
+ savedDevice?.reboot()
+ savedDevice = null
+
+ if (DEBUG_NO_REBOOT) {
+ savedPreparer?.after()
+ savedPreparer = null
+ }
+ }
+ }
+
+ private val tempFolder = TemporaryFolder()
+ private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+ SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device }
+
+ @get:Rule
+ val rules = RuleChain.outerRule(tempFolder).let {
+ if (DEBUG_NO_REBOOT) {
+ it!!
+ } else {
+ it.around(preparer)!!
+ }
+ }
+
+ private var hostCompressedFile: File? = null
+
+ private val previousCodePaths = mutableListOf<String>()
+
+ @Before
+ fun ensureUserAndCompressStubAndInstall() {
+ setUpUsers(device)
+
+ val initialized = hostCompressedFile != null
+ if (!initialized) {
+ hostCompressedFile = tempFolder.newFile()
+ hostCompressedFile!!.outputStream().use {
+ javaClass.classLoader
+ .getResource(VERSION_ONE)!!
+ .openStream()
+ .use { input ->
+ GZIPOutputStream(it).use { output ->
+ input.copyTo(output)
+ }
+ }
+ }
+ }
+
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ if (!initialized || !DEBUG_NO_REBOOT) {
+ savedPreparer = preparer
+ preparer.pushResourceFile(VERSION_STUB, stubFile.toString())
+ .pushFile(hostCompressedFile, deviceCompressedFile.toString())
+ .reboot()
+ }
+
+ // This test forces the state to installed/enabled for all users,
+ // since it only tests the uninstall/disable side.
+ installExisting(User.PRIMARY)
+ installExisting(User.SECONDARY)
+
+ ensureEnabled()
+
+ // Ensure data app isn't re-installed multiple times by comparing against the original path
+ val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first()
+ assertThat(codePath).contains("/data/app")
+ assertThat(codePath).contains(TEST_PKG_NAME)
+
+ previousCodePaths.clear()
+ previousCodePaths += codePath
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disablePrimaryFirstAndUninstall() {
+ toggleEnabled(false, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(false, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disableSecondaryFirstAndUninstall() {
+ toggleEnabled(false, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(false, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstalledEnablePrimaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstalledEnableSecondaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+ device.uninstallPackage(TEST_PKG_NAME)
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() {
+ uninstall(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ uninstall(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ installExisting(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ installExisting(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() {
+ uninstall(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ uninstall(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ installExisting(User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ installExisting(User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallUpdatesAndEnablePrimaryFirst() {
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ // TODO: Is this intentional? There is no user argument for this command.
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ // If any user is enabled when uninstalling updates, /data is re-uncompressed
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ // Test enabling secondary to ensure path does not change, even though it's already enabled
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstallUpdatesAndEnableSecondaryFirst() {
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ // If any user is enabled when uninstalling updates, /data is re-uncompressed
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstallUpdatesAndEnablePrimaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun disabledUninstallUpdatesAndEnableSecondaryFirst() {
+ toggleEnabled(false, User.PRIMARY)
+ toggleEnabled(false, User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = false,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = false,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = true, primaryEnabled = true,
+ secondaryInstalled = true, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() {
+ uninstall(User.PRIMARY)
+ uninstall(User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ assertState(
+ primaryInstalled = false, primaryEnabled = false,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ @Test
+ fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() {
+ uninstall(User.PRIMARY)
+ uninstall(User.SECONDARY)
+
+ device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+ // Uninstall-system-updates always disables system user 0
+ assertState(
+ primaryInstalled = false, primaryEnabled = false,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.SECONDARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = false,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+ )
+
+ toggleEnabled(true, User.PRIMARY)
+
+ assertState(
+ primaryInstalled = false, primaryEnabled = true,
+ secondaryInstalled = false, secondaryEnabled = true,
+ codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+ )
+ }
+
+ private fun ensureEnabled() {
+ toggleEnabled(true, User.PRIMARY)
+ toggleEnabled(true, User.SECONDARY)
+
+ assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value })
+ .isTrue()
+ }
+
+ private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) {
+ val command = if (enabled) "enable" else "disable"
+ @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+ User.PRIMARY -> {
+ device.executeShellCommand("pm $command --user 0 $pkgName")
+ }
+ User.SECONDARY -> {
+ secondaryUsers.forEach {
+ device.executeShellCommand("pm $command --user $it $pkgName")
+ }
+ }
+ }
+ }
+
+ private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) {
+ @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+ User.PRIMARY -> {
+ device.executeShellCommand("pm uninstall --user 0 $pkgName")
+ }
+ User.SECONDARY -> {
+ secondaryUsers.forEach {
+ device.executeShellCommand("pm uninstall --user $it $pkgName")
+ }
+ }
+ }
+ }
+
+ private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) {
+ @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+ User.PRIMARY -> {
+ device.executeShellCommand("pm install-existing --user 0 $pkgName")
+ }
+ User.SECONDARY -> {
+ secondaryUsers.forEach {
+ device.executeShellCommand("pm install-existing --user $it $pkgName")
+ }
+ }
+ }
+ }
+
+ private fun assertState(
+ primaryInstalled: Boolean,
+ primaryEnabled: Boolean,
+ secondaryInstalled: Boolean,
+ secondaryEnabled: Boolean,
+ codePaths: List<CodePath>
+ ) {
+ HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME)
+ .forEach { (userId, installed) ->
+ if (userId == 0) {
+ assertThat(installed).isEqualTo(primaryInstalled)
+ } else {
+ assertThat(installed).isEqualTo(secondaryInstalled)
+ }
+ }
+
+ HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME)
+ .forEach { (userId, enabled) ->
+ if (userId == 0) {
+ assertThat(enabled).isEqualTo(primaryEnabled)
+ } else {
+ assertThat(enabled).isEqualTo(secondaryEnabled)
+ }
+ }
+
+ assertCodePaths(codePaths.first(), codePaths.getOrNull(1))
+ }
+
+ private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) {
+ val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME)
+ assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size)
+
+ when (firstCodePath) {
+ CodePath.SAME -> {
+ assertThat(codePaths[0]).contains("/data/app")
+ assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+ assertThat(codePaths[0]).isEqualTo(previousCodePaths.last())
+ }
+ CodePath.DIFFERENT -> {
+ assertThat(codePaths[0]).contains("/data/app")
+ assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+ assertThat(previousCodePaths).doesNotContain(codePaths[0])
+ previousCodePaths.add(codePaths[0])
+ }
+ CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString())
+ }
+
+ when (secondCodePath) {
+ CodePath.SAME, CodePath.DIFFERENT ->
+ throw AssertionError("secondDataPath cannot be a data path")
+ CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+ }
+ }
+
+ enum class User {
+ /** The primary system user 0 */
+ PRIMARY,
+
+ /**
+ * All other users on the device that are not 0. This is split into an enum so that all
+ * methods that handle secondary act on all non-system users. Some behaviors only occur
+ * if a package state is marked for all non-primary users on the device, which can be
+ * more than just 1.
+ */
+ SECONDARY
+ }
+
+ enum class CodePath {
+ /** The data code path hasn't changed */
+ SAME,
+
+ /** New data code path */
+ DIFFERENT,
+
+ /** The static system code path */
+ SYSTEM
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
index c9b2927..4a3076e 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
@@ -13,26 +13,32 @@
// limitations under the License.
android_test_helper_app {
- name: "PackageManagerDummyAppVersion1",
+ name: "PackageManagerTestAppStub",
+ manifest: "AndroidManifestVersion1.xml",
+ srcs: []
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestAppVersion1",
manifest: "AndroidManifestVersion1.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppVersion2",
+ name: "PackageManagerTestAppVersion2",
manifest: "AndroidManifestVersion2.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppVersion3",
+ name: "PackageManagerTestAppVersion3",
manifest: "AndroidManifestVersion3.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppVersion4",
+ name: "PackageManagerTestAppVersion4",
manifest: "AndroidManifestVersion4.xml"
}
android_test_helper_app {
- name: "PackageManagerDummyAppOriginalOverride",
+ name: "PackageManagerTestAppOriginalOverride",
manifest: "AndroidManifestOriginalOverride.xml"
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
index f16e1bc..cba580e 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
@@ -16,10 +16,10 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app.override"
+ package="com.android.server.pm.test.test_app.override"
android:versionCode="2"
>
- <original-package android:name="com.android.server.pm.test.dummy_app"/>
+ <original-package android:name="com.android.server.pm.test.test_app"/>
</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
index b492a31..efc7372 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="1"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
index 25e9f8e..620054c 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="2"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
index 935f5e6..1997771 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="3"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
index d0643cb..d6ade03 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
@@ -16,12 +16,12 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.pm.test.dummy_app"
+ package="com.android.server.pm.test.test_app"
android:versionCode="4"
>
<permission
- android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+ android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
android:protectionLevel="normal"
/>
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 2a267c4..1b2711d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -99,6 +99,7 @@
import org.junit.BeforeClass;
import org.junit.Test;
+import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -165,7 +166,7 @@
setFieldValue(ActivityManagerService.class, sService, "mHandler",
mock(ActivityManagerService.MainHandler.class));
setFieldValue(ActivityManagerService.class, sService, "mProcessStats",
- mock(ProcessStatsService.class));
+ new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats")));
setFieldValue(ActivityManagerService.class, sService, "mBackupTargets",
mock(SparseArray.class));
setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler",
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index f991dff..db56657 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -550,7 +550,6 @@
.setAppId(DUMMY_TARGET_APPID)
.setName("com.some.package")
.setCodePath("/")
- .setResourcePath("/")
.setPVersionCode(1L)
.build();
PackageSetting calling = simulateAddPackage(appsFilter,
@@ -874,7 +873,6 @@
.setAppId(appId)
.setName(newPkg.getPackageName())
.setCodePath("/")
- .setResourcePath("/")
.setPVersionCode(1L);
final PackageSetting setting =
(action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 164bd72..90edaef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -38,8 +38,7 @@
public PackageSetting generateFakePackageSetting(String name) {
return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
- new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
- "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
+ "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index c4e25b5..80f145b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -86,8 +86,7 @@
// Create a real (non-null) PackageSetting and confirm that the removed
// users are copied properly
setting = new PackageSetting("name", "realName", new File("codePath"),
- new File("resourcePath"), "legacyNativeLibraryPathString",
- "primaryCpuAbiString", "secondaryCpuAbiString",
+ "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString",
"cpuAbiOverrideString", 0, 0, 0, 0,
null, null, null);
pri.populateUsers(new int[] {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index aa92ba4..0bf06bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -439,7 +439,6 @@
PACKAGE_NAME,
REAL_PACKAGE_NAME,
INITIAL_CODE_PATH /*codePath*/,
- INITIAL_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPathString*/,
"x86_64" /*primaryCpuAbiString*/,
"x86" /*secondaryCpuAbiString*/,
@@ -461,7 +460,6 @@
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
INITIAL_CODE_PATH /*codePath*/,
- INITIAL_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPathString*/,
"x86_64" /*primaryCpuAbiString*/,
"x86" /*secondaryCpuAbiString*/,
@@ -477,7 +475,6 @@
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
UPDATED_CODE_PATH /*codePath*/,
- UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPathString*/,
null /*primaryCpuAbiString*/,
null /*secondaryCpuAbiString*/,
@@ -507,7 +504,6 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -541,7 +537,6 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -581,7 +576,6 @@
null /*disabledPkg*/,
testUserSetting01 /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -609,7 +603,6 @@
null /*realPkgName*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -624,12 +617,11 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/);
- assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+ assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
- assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
// signatures object must be different
assertNotSame(testPkgSetting01.signatures, originalSignatures);
@@ -649,7 +641,6 @@
null /*realPkgName*/,
null /*sharedUser*/,
INITIAL_CODE_PATH /*codePath*/,
- INITIAL_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"x86_64" /*primaryCpuAbiString*/,
"x86" /*secondaryCpuAbiString*/,
@@ -665,12 +656,11 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/);
assertThat(testPkgSetting01.appId, is(0));
- assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+ assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
- assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
// by default, the package is considered stopped
@@ -695,7 +685,6 @@
null /*realPkgName*/,
testUserSetting01 /*sharedUser*/,
INITIAL_CODE_PATH /*codePath*/,
- INITIAL_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"x86_64" /*primaryCpuAbiString*/,
"x86" /*secondaryCpuAbiString*/,
@@ -711,12 +700,11 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/);
assertThat(testPkgSetting01.appId, is(10064));
- assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+ assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
- assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -738,7 +726,6 @@
null /*realPkgName*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -754,12 +741,11 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/);
assertThat(testPkgSetting01.appId, is(10064));
- assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+ assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
- assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
assertNotSame(testPkgSetting01.signatures, disabledSignatures);
assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
@@ -806,10 +792,10 @@
private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
assertThat(origPkgSetting, is(not(testPkgSetting)));
assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
- assertSame(origPkgSetting.codePath, testPkgSetting.codePath);
- assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath));
- assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString);
- assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString));
+ assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath());
+ assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath()));
+ assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString());
+ assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString()));
assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
@@ -823,7 +809,9 @@
testPkgSetting.legacyNativeLibraryPathString);
assertThat(origPkgSetting.legacyNativeLibraryPathString,
is(testPkgSetting.legacyNativeLibraryPathString));
- assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+ if (origPkgSetting.mimeGroups != null) {
+ assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+ }
assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
@@ -839,10 +827,6 @@
assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString);
assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString));
assertThat(origPkgSetting.realName, is(testPkgSetting.realName));
- assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath);
- assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath));
- assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString);
- assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString));
assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString);
assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString));
assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser);
@@ -874,7 +858,6 @@
PACKAGE_NAME,
REAL_PACKAGE_NAME,
INITIAL_CODE_PATH /*codePath*/,
- INITIAL_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPathString*/,
"x86_64" /*primaryCpuAbiString*/,
"x86" /*secondaryCpuAbiString*/,
@@ -893,7 +876,6 @@
packageName,
packageName,
INITIAL_CODE_PATH /*codePath*/,
- INITIAL_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPathString*/,
"x86_64" /*primaryCpuAbiString*/,
"x86" /*secondaryCpuAbiString*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index b0b5386..2651cfa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -312,8 +312,7 @@
private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
- new File(pkg.getCodePath()), new File(pkg.getCodePath()), null,
- pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
+ new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
null, pkg.getVersionCode(),
PackageInfoUtils.appInfoFlags(pkg, null),
PackageInfoUtils.appInfoPrivateFlags(pkg, null),
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index d12ea894..f8e92ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -30,7 +30,6 @@
private String mName;
private String mRealName;
private String mCodePath;
- private String mResourcePath;
private String mLegacyNativeLibraryPathString;
private String mPrimaryCpuAbiString;
private String mSecondaryCpuAbiString;
@@ -74,11 +73,6 @@
return this;
}
- public PackageSettingBuilder setResourcePath(String resourcePath) {
- this.mResourcePath = resourcePath;
- return this;
- }
-
public PackageSettingBuilder setLegacyNativeLibraryPathString(
String legacyNativeLibraryPathString) {
this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString;
@@ -162,10 +156,10 @@
public PackageSetting build() {
final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
- new File(mCodePath), new File(mResourcePath),
- mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
- mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId,
- mUsesStaticLibraries, mUsesStaticLibrariesVersions, mMimeGroups);
+ new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
+ mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
+ mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
+ mMimeGroups);
packageSetting.signatures = mSigningDetails != null
? new PackageSignatures(mSigningDetails)
: new PackageSignatures();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 55bc681..7108490 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -465,9 +465,9 @@
// Generic PackageSetting object with values from a test app installed on a device to be
// used to test the methods under the PackageSignatures signatures data member.
File appPath = new File("/data/app/app");
- PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
- "/data/app/app", null, null, null,
- 1, 940097092, 0, 0 /*userId*/, null, null, null /*mimeGroups*/);
+ PackageSetting result = new PackageSetting("test.app", null, appPath,
+ "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null,
+ null /*mimeGroups*/);
return result;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index e7eff00..56dddb0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -484,8 +484,7 @@
private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) {
return new PackageSettingBuilder()
.setName(packageName)
- .setCodePath(createCodePath(packageName))
- .setResourcePath(createCodePath(packageName));
+ .setCodePath(createCodePath(packageName));
}
private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) {
@@ -534,8 +533,7 @@
arrayContaining("some.static.library", "some.other.static.library"));
assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
- assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
- assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName))));
+ assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName))));
assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 4040fa6..e3c795d 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -38,6 +38,7 @@
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.STATUS_BAR" />
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index addf1ff..96b9700 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -38,7 +38,13 @@
import android.app.WaitResult;
import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -173,4 +179,50 @@
verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
eq(true) /* focused */);
}
+
+ @Test
+ /** Ensures that a trusted virtual display can launch arbitrary activities. */
+ public void testTrustedVirtualDisplayCanLaunchActivities() {
+ final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+ final Task stack = new StackBuilder(mRootWindowContainer)
+ .setDisplay(newDisplay).build();
+ final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
+ VirtualDisplay virtualDisplay = createVirtualDisplay(true);
+ final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
+ virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+
+ assertThat(allowed).isTrue();
+ }
+
+ @Test
+ /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */
+ public void testUntrustedVirtualDisplayCannotLaunchActivities() {
+ final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+ final Task stack = new StackBuilder(mRootWindowContainer)
+ .setDisplay(newDisplay).build();
+ final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
+ VirtualDisplay virtualDisplay = createVirtualDisplay(false);
+ final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
+ virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+
+ assertThat(allowed).isFalse();
+ }
+
+ private VirtualDisplay createVirtualDisplay(boolean trusted) {
+ final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ defaultDisplay.getDisplayInfo(displayInfo);
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ if (trusted) {
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+ }
+
+ final ImageReader imageReader = ImageReader.newInstance(
+ displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+ return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth,
+ displayInfo.logicalHeight,
+ displayInfo.logicalDensityDpi, imageReader.getSurface(), flags);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index d950344..b4a1337 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -87,7 +88,7 @@
0 /* systemUiVisibility */, false /* isTranslucent */);
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
- taskBounds, ORIENTATION_PORTRAIT, new InsetsState());
+ taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState());
}
private static TaskDescription createTaskDescription(int background, int statusBar,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index e078f26..c2ebcc2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -102,7 +102,8 @@
eventLog {
focusChanges(testApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity")
+ "recents_animation_input_consumer", "NexusLauncherActivity",
+ bugId = 151179149)
}
}
}
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index f30c35a..c2a5459 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -34,6 +34,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import javax.annotation.Nullable;
@@ -49,7 +51,7 @@
public class SystemPreparer extends ExternalResource {
private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
- // The paths of the files pushed onto the device through this rule.
+ // The paths of the files pushed onto the device through this rule to be removed after.
private ArrayList<String> mPushedFiles = new ArrayList<>();
// The package names of packages installed through this rule.
@@ -81,7 +83,7 @@
final ITestDevice device = mDeviceProvider.getDevice();
remount();
assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath));
- mPushedFiles.add(outputPath);
+ addPushedFile(device, outputPath);
return this;
}
@@ -91,10 +93,23 @@
final ITestDevice device = mDeviceProvider.getDevice();
remount();
assertTrue(device.pushFile(file, outputPath));
- mPushedFiles.add(outputPath);
+ addPushedFile(device, outputPath);
return this;
}
+ private void addPushedFile(ITestDevice device, String outputPath)
+ throws DeviceNotAvailableException {
+ Path pathCreated = Paths.get(outputPath);
+
+ // Find the top most parent that is new to the device
+ while (pathCreated.getParent() != null
+ && !device.doesFileExist(pathCreated.getParent().toString())) {
+ pathCreated = pathCreated.getParent();
+ }
+
+ mPushedFiles.add(pathCreated.toString());
+ }
+
/** Deletes the given path from the device */
public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException {
final ITestDevice device = mDeviceProvider.getDevice();
@@ -203,7 +218,7 @@
/** Removes installed packages and files that were pushed to the device. */
@Override
- protected void after() {
+ public void after() {
final ITestDevice device = mDeviceProvider.getDevice();
try {
remount();