Merge "Add dynamic item in output switcher if it is available"
diff --git a/api/system-current.txt b/api/system-current.txt
index 5ef042f..9cf0926 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5725,7 +5725,7 @@
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
- public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+ public final class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
method public int getBandwidthCapability();
method public int getCodeRateCapability();
method public int getGuardIntervalCapability();
@@ -5734,7 +5734,7 @@
method public int getTransmissionModeCapability();
}
- public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+ public final class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
method public int getBandwidth();
method public int getCodeRate();
@@ -5797,6 +5797,7 @@
public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder();
method public int getAnnex();
+ method public int getBandwidth();
method public long getInnerFec();
method public int getModulation();
method public int getOuterFec();
@@ -5808,6 +5809,11 @@
field public static final int ANNEX_B = 2; // 0x2
field public static final int ANNEX_C = 4; // 0x4
field public static final int ANNEX_UNDEFINED = 0; // 0x0
+ field public static final int BANDWIDTH_5MHZ = 1; // 0x1
+ field public static final int BANDWIDTH_6MHZ = 2; // 0x2
+ field public static final int BANDWIDTH_7MHZ = 4; // 0x4
+ field public static final int BANDWIDTH_8MHZ = 8; // 0x8
+ field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
field public static final int MODULATION_AUTO = 1; // 0x1
field public static final int MODULATION_MOD_128QAM = 16; // 0x10
field public static final int MODULATION_MOD_16QAM = 2; // 0x2
@@ -5837,6 +5843,7 @@
public static class DvbcFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setBandwidth(int);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
@@ -6130,17 +6137,17 @@
method public int getBer();
method @NonNull public int[] getBers();
method @NonNull public int[] getCodeRates();
+ method @NonNull public int[] getExtendedModulations();
method public int getFreqOffset();
method public int getGuardInterval();
method public int getHierarchy();
method public long getInnerFec();
method @NonNull public int[] getInterleaving();
- method @NonNull public int[] getIsdbtSegment();
+ method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
method @NonNull public boolean[] getLayerErrors();
method public int getLnbVoltage();
method public int getMer();
method public int getModulation();
- method @NonNull public int[] getModulationsExt();
method public int getPer();
method public int getPerBer();
method public int getPlpId();
@@ -6149,7 +6156,7 @@
method public int getSnr();
method public int getSpectralInversion();
method public int getSymbolRate();
- method public int getSystemId();
+ method @IntRange(from=0, to=65535) public int getSystemId();
method public int getTransmissionMode();
method @NonNull public int[] getTsDataRate();
method public int getUec();
@@ -9478,6 +9485,7 @@
field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
+ field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
field public static final String NAMESPACE_RUNTIME = "runtime";
diff --git a/api/test-current.txt b/api/test-current.txt
index edc422d..67b496b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1802,6 +1802,7 @@
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index eea50be..b37f738 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5665,7 +5665,7 @@
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
- public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+ public final class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
method public int getBandwidthCapability();
method public int getCodeRateCapability();
method public int getGuardIntervalCapability();
@@ -5674,7 +5674,7 @@
method public int getTransmissionModeCapability();
}
- public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+ public final class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
method public int getBandwidth();
method public int getCodeRate();
@@ -5737,6 +5737,7 @@
public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder();
method public int getAnnex();
+ method public int getBandwidth();
method public long getInnerFec();
method public int getModulation();
method public int getOuterFec();
@@ -5748,6 +5749,11 @@
field public static final int ANNEX_B = 2; // 0x2
field public static final int ANNEX_C = 4; // 0x4
field public static final int ANNEX_UNDEFINED = 0; // 0x0
+ field public static final int BANDWIDTH_5MHZ = 1; // 0x1
+ field public static final int BANDWIDTH_6MHZ = 2; // 0x2
+ field public static final int BANDWIDTH_7MHZ = 4; // 0x4
+ field public static final int BANDWIDTH_8MHZ = 8; // 0x8
+ field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
field public static final int MODULATION_AUTO = 1; // 0x1
field public static final int MODULATION_MOD_128QAM = 16; // 0x10
field public static final int MODULATION_MOD_16QAM = 2; // 0x2
@@ -5777,6 +5783,7 @@
public static class DvbcFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setBandwidth(int);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
@@ -6070,17 +6077,17 @@
method public int getBer();
method @NonNull public int[] getBers();
method @NonNull public int[] getCodeRates();
+ method @NonNull public int[] getExtendedModulations();
method public int getFreqOffset();
method public int getGuardInterval();
method public int getHierarchy();
method public long getInnerFec();
method @NonNull public int[] getInterleaving();
- method @NonNull public int[] getIsdbtSegment();
+ method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
method @NonNull public boolean[] getLayerErrors();
method public int getLnbVoltage();
method public int getMer();
method public int getModulation();
- method @NonNull public int[] getModulationsExt();
method public int getPer();
method public int getPerBer();
method public int getPlpId();
@@ -6089,7 +6096,7 @@
method public int getSnr();
method public int getSpectralInversion();
method public int getSymbolRate();
- method public int getSystemId();
+ method @IntRange(from=0, to=65535) public int getSystemId();
method public int getTransmissionMode();
method @NonNull public int[] getTsDataRate();
method public int getUec();
@@ -8329,6 +8336,7 @@
field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
+ field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
field public static final String NAMESPACE_RUNTIME = "runtime";
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..a16bb4f
--- /dev/null
+++ b/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+per-file PackageParser.java = chiuwinson@google.com
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 118524d..1c90d23 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -141,8 +141,14 @@
* <li>All installations must contain a single base APK.
* </ul>
*
+ * @deprecated This class is mostly unused and no new changes should be added to it. Use
+ * {@link android.content.pm.parsing.ParsingPackageUtils} and related parsing v2 infrastructure in
+ * the core/services parsing subpackages. Or for a quick parse of a provided APK, use
+ * {@link PackageManager#getPackageArchiveInfo(String, int)}.
+ *
* @hide
*/
+@Deprecated
public class PackageParser {
public static final boolean DEBUG_JAR = false;
diff --git a/core/java/android/content/pm/parsing/OWNERS b/core/java/android/content/pm/parsing/OWNERS
new file mode 100644
index 0000000..8049d5c
--- /dev/null
+++ b/core/java/android/content/pm/parsing/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+
+chiuwinson@google.com
+patb@google.com
+toddke@google.com
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
index 8345ff3..98e8c92 100644
--- a/core/java/android/hardware/usb/AccessoryFilter.java
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -102,7 +102,7 @@
public boolean matches(UsbAccessory acc) {
if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
if (mModel != null && !acc.getModel().equals(mModel)) return false;
- return !(mVersion != null && !acc.getVersion().equals(mVersion));
+ return !(mVersion != null && !mVersion.equals(acc.getVersion()));
}
/**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 0a4e867..97c9f4b 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -251,6 +251,15 @@
public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
/**
+ * Namespace for features related to the Profcollect native Service.
+ * These features are applied at reboot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
+
+ /**
* Namespace for Rollback flags that are applied immediately.
*
* @hide
diff --git a/core/java/com/android/internal/app/ChooserFlags.java b/core/java/com/android/internal/app/ChooserFlags.java
index 3e26679..1a93f1b 100644
--- a/core/java/com/android/internal/app/ChooserFlags.java
+++ b/core/java/com/android/internal/app/ChooserFlags.java
@@ -33,7 +33,7 @@
*/
public static final boolean USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, true);
+ SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, false);
/**
* Whether to use {@link AppPredictionManager} to query for direct share targets (as opposed to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f44ebf3..833924c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,7 +63,6 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipUpdateThread;
@@ -137,7 +136,6 @@
private final PipBoundsState mPipBoundsState;
private final PipBoundsHandler mPipBoundsHandler;
private final PipMenuActivityController mMenuActivityController;
- private final SystemWindows mSystemWindows;
private final PipAnimationController mPipAnimationController;
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -270,14 +268,12 @@
Optional<SplitScreen> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
- @NonNull ShellTaskOrganizer shellTaskOrganizer,
- @NonNull SystemWindows systemWindows) {
+ @NonNull ShellTaskOrganizer shellTaskOrganizer) {
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mPipBoundsState = pipBoundsState;
mPipBoundsHandler = boundsHandler;
mMenuActivityController = menuActivityController;
- mSystemWindows = systemWindows;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = surfaceTransactionHelper;
@@ -499,9 +495,13 @@
}
if (mShouldIgnoreEnteringPipTransition) {
+ final Rect destinationBounds = mPipBoundsState.getBounds();
// animation is finished in the Launcher and here we directly apply the final touch.
- applyEnterPipSyncTransaction(mPipBoundsState.getBounds(),
- () -> sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP));
+ applyEnterPipSyncTransaction(destinationBounds, () -> {
+ // ensure menu's settled in its final bounds first
+ finishResizeForMenu(destinationBounds);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ });
mShouldIgnoreEnteringPipTransition = false;
return;
}
@@ -995,16 +995,21 @@
return;
} else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
// TODO: Synchronize this correctly in #applyEnterPipSyncTransaction
- runOnMainHandler(() -> {
- mMenuActivityController.movePipMenu(null, null, destinationBounds);
- mMenuActivityController.updateMenuBounds(destinationBounds);
- });
+ finishResizeForMenu(destinationBounds);
return;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
prepareFinishResizeTransaction(destinationBounds, direction, tx, wct);
applyFinishBoundsResize(wct, direction);
+ finishResizeForMenu(destinationBounds);
+ }
+
+ private void finishResizeForMenu(Rect destinationBounds) {
+ if (mMenuActivityController == null) {
+ if (DEBUG) Log.d(TAG, "mMenuActivityController is null");
+ return;
+ }
runOnMainHandler(() -> {
mMenuActivityController.movePipMenu(null, null, destinationBounds);
mMenuActivityController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
index a525265..24144b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
@@ -305,8 +305,8 @@
}
private boolean maybeCreateSyncApplier() {
- if (mPipMenuView == null) {
- Log.v(TAG, "Not going to move PiP since the menu is not created.");
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ Log.v(TAG, "Not going to move PiP, either menu or its parent is not created.");
return false;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index ccc6797..39381c6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -41,7 +41,6 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -69,7 +68,6 @@
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
- @Mock private SystemWindows mSystemWindows;
private PipBoundsState mPipBoundsState;
private ComponentName mComponent1;
@@ -84,7 +82,7 @@
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
mMockPipBoundsHandler, mMenuActivityController, mMockPipSurfaceTransactionHelper,
mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger,
- mMockShellTaskOrganizer, mSystemWindows));
+ mMockShellTaskOrganizer));
preparePipTaskOrg();
}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 1d06e28..a2861c2 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -943,7 +943,7 @@
* with named endpoint channels. The samples in the frame correspond to the
* named set bits in the channel position mask, in ascending bit order.
* See {@link #setChannelIndexMask(int)} to specify channels
- * based on endpoint numbered channels. This <a href="#channelPositionMask>description of
+ * based on endpoint numbered channels. This <a href="#channelPositionMask">description of
* channel position masks</a> covers the concept in more details.
* @param channelMask describes the configuration of the audio channels.
* <p> For output, the channelMask can be an OR-ed combination of
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
index 9fc3a23..dd386b0 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
@@ -25,7 +25,7 @@
* @hide
*/
@SystemApi
-public class DtmbFrontendCapabilities extends FrontendCapabilities {
+public final class DtmbFrontendCapabilities extends FrontendCapabilities {
private final int mModulationCap;
private final int mTransmissionModeCap;
private final int mGuardIntervalCap;
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
index 2c3fe6a..d85e60d 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -34,7 +34,7 @@
* @hide
*/
@SystemApi
-public class DtmbFrontendSettings extends FrontendSettings {
+public final class DtmbFrontendSettings extends FrontendSettings {
/** @hide */
@IntDef(flag = true,
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index e6968bb..a611da6 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -223,6 +223,40 @@
public static final int TIME_INTERLEAVE_MODE_128_4 = android.hardware.tv.tuner.V1_1.Constants
.FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "BANDWIDTH_",
+ value = {BANDWIDTH_UNDEFINED, BANDWIDTH_5MHZ, BANDWIDTH_6MHZ, BANDWIDTH_7MHZ,
+ BANDWIDTH_8MHZ})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Bandwidth {}
+
+ /**
+ * Bandwidth undefined.
+ */
+ public static final int BANDWIDTH_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.UNDEFINED;
+ /**
+ * 5 MHz bandwidth.
+ */
+ public static final int BANDWIDTH_5MHZ =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_5MHZ;
+ /**
+ * 6 MHz bandwidth.
+ */
+ public static final int BANDWIDTH_6MHZ =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_6MHZ;
+ /**
+ * 7 MHz bandwidth.
+ */
+ public static final int BANDWIDTH_7MHZ =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_7MHZ;
+ /**
+ * 8 MHz bandwidth.
+ */
+ public static final int BANDWIDTH_8MHZ =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_8MHZ;
+
private final int mModulation;
private final long mInnerFec;
@@ -232,9 +266,11 @@
private final int mSpectralInversion;
// Dvbc time interleave mode is only supported in Tuner 1.1 or higher.
private final int mInterleaveMode;
+ // Dvbc bandwidth is only supported in Tuner 1.1 or higher.
+ private final int mBandwidth;
private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
- int outerFec, int annex, int spectralInversion, int interleaveMode) {
+ int outerFec, int annex, int spectralInversion, int interleaveMode, int bandwidth) {
super(frequency);
mModulation = modulation;
mInnerFec = innerFec;
@@ -243,6 +279,7 @@
mAnnex = annex;
mSpectralInversion = spectralInversion;
mInterleaveMode = interleaveMode;
+ mBandwidth = bandwidth;
}
/**
@@ -293,6 +330,13 @@
public int getTimeInterleaveMode() {
return mInterleaveMode;
}
+ /**
+ * Gets Bandwidth.
+ */
+ @Bandwidth
+ public int getBandwidth() {
+ return mBandwidth;
+ }
/**
* Creates a builder for {@link DvbcFrontendSettings}.
@@ -314,6 +358,7 @@
private int mAnnex = ANNEX_UNDEFINED;
private int mSpectralInversion = FrontendSettings.FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
private int mInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
+ private int mBandwidth = BANDWIDTH_UNDEFINED;
private Builder() {
}
@@ -407,6 +452,23 @@
}
return this;
}
+ /**
+ * Set the Bandwidth.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param bandwidth the value to set as the bandwidth. Default value is
+ * {@link #BANDWIDTH_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setBandwidth(@Bandwidth int bandwidth) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setBandwidth")) {
+ mBandwidth = bandwidth;
+ }
+ return this;
+ }
/**
* Builds a {@link DvbcFrontendSettings} object.
@@ -414,7 +476,7 @@
@NonNull
public DvbcFrontendSettings build() {
return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
- mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode);
+ mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode, mBandwidth);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index dd9347c..c93e079 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -634,7 +635,7 @@
}
/**
- * Gets an array of BERS status.
+ * Gets an array of extended bit error ratio status.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
@@ -650,12 +651,14 @@
}
/**
- * Gets an array of code rates status.
+ * Gets an array of code rates status. The {@link FrontendSettings.InnerFec} would be used to
+ * show the code rate.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
+ @FrontendSettings.InnerFec
public int[] getCodeRates() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "getCodeRates status");
@@ -714,7 +717,8 @@
}
/**
- * Gets UEC status.
+ * Gets the Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) since the
+ * last tune operation.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
@@ -729,11 +733,12 @@
}
/**
- * Gets system id status.
+ * Gets the current DVB-T2 system id status.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
+ @IntRange(from = 0, to = 0xffff)
public int getSystemId() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "getSystemId status");
@@ -744,13 +749,14 @@
}
/**
- * Gets an array of interleaving status. Array value should be withink {@link
+ * Gets an array of interleaving status. Array value should be within {@link
* FrontendInterleaveMode}.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
+ @FrontendInterleaveMode
public int[] getInterleaving() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "getInterleaving status");
@@ -761,12 +767,13 @@
}
/**
- * Gets an array of isdbt segment status.
+ * Gets an array of the segments status in ISDB-T Specification of all the channels.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
+ @IntRange(from = 0, to = 0xff)
public int[] getIsdbtSegment() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "getIsdbtSegment status");
@@ -777,7 +784,7 @@
}
/**
- * Gets an array of TS data rate status.
+ * Gets an array of the Transport Stream Data Rate in BPS of the current channel.
*
* <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
@@ -800,9 +807,9 @@
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
- public int[] getModulationsExt() {
+ public int[] getExtendedModulations() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_1_1, "getModulationsExt status");
+ TunerVersionChecker.TUNER_VERSION_1_1, "getExtendedModulations status");
if (mModulationsExt == null) {
throw new IllegalStateException();
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 1be0d44..27e1992 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -140,6 +140,7 @@
using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1;
using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbsSettingsExt1_1;
@@ -2725,9 +2726,13 @@
FrontendCableTimeInterleaveMode interleaveMode =
static_cast<FrontendCableTimeInterleaveMode>(
env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I")));
+ FrontendDvbcBandwidth bandwidth =
+ static_cast<FrontendDvbcBandwidth>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
FrontendDvbcSettingsExt1_1 dvbcExt1_1 {
.interleaveMode = interleaveMode,
+ .bandwidth = bandwidth,
};
settingsExt1_1.settingExt.dvbc(dvbcExt1_1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 64a2aca..e40185c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -32,6 +32,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -67,7 +68,7 @@
private final int mTapTimeout = ViewConfiguration.getTapTimeout();
private final int mTouchSlop;
private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
- private final WindowManager.LayoutParams mParams;
+ private final LayoutParams mParams;
private boolean mIsVisible = false;
MagnificationModeSwitch(Context context) {
@@ -80,7 +81,7 @@
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
- mParams = createLayoutParams();
+ mParams = createLayoutParams(context);
mImageView = imageView;
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
applyResourcesValues();
@@ -221,11 +222,22 @@
}
void onConfigurationChanged(int configDiff) {
- if ((configDiff & ActivityInfo.CONFIG_DENSITY) == 0) {
+ if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
+ applyResourcesValues();
+ mImageView.setImageResource(getIconResId(mMagnificationMode));
return;
}
- applyResourcesValues();
- mImageView.setImageResource(getIconResId(mMagnificationMode));
+ if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ updateAccessibilityWindowTitle();
+ return;
+ }
+ }
+
+ private void updateAccessibilityWindowTitle() {
+ mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
+ if (mIsVisible) {
+ mWindowManager.updateViewLayout(mImageView, mParams);
+ }
}
private void toggleMagnificationMode() {
@@ -261,14 +273,19 @@
: R.drawable.ic_open_in_new_fullscreen;
}
- private static WindowManager.LayoutParams createLayoutParams() {
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ private static LayoutParams createLayoutParams(Context context) {
+ final LayoutParams params = new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ params.accessibilityTitle = getAccessibilityWindowTitle(context);
return params;
}
+
+ private static String getAccessibilityWindowTitle(Context context) {
+ return context.getString(com.android.internal.R.string.android_system_label);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index e9e453b..98424be 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -53,7 +53,8 @@
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
private static final int CONFIG_MASK =
- ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION;
+ ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION
+ | ActivityInfo.CONFIG_LOCALE;
@VisibleForTesting
protected WindowMagnificationAnimationController mWindowMagnificationAnimationController;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 340ca04..fd89baa 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,7 +16,7 @@
package com.android.systemui.accessibility;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
@@ -254,9 +254,18 @@
}
} else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
onRotate();
+ } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ updateAccessibilityWindowTitleIfNeeded();
}
}
+ private void updateAccessibilityWindowTitleIfNeeded() {
+ if (!isWindowVisible()) return;
+ LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
+ params.accessibilityTitle = getAccessibilityWindowTitle();
+ mWm.updateViewLayout(mMirrorView, params);
+ }
+
/** Handles MirrorWindow position when the navigation bar mode changed. */
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
@@ -290,8 +299,8 @@
return;
}
// The rect of MirrorView is going to be transformed.
- WindowManager.LayoutParams params =
- (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+ LayoutParams params =
+ (LayoutParams) mMirrorView.getLayoutParams();
mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height);
final RectF transformedRect = new RectF(mTmpRect);
matrix.mapRect(transformedRect);
@@ -313,17 +322,18 @@
int windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
int windowHeight = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
- WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ LayoutParams params = new LayoutParams(
windowWidth, windowHeight,
- WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
params.setTitle(mContext.getString(R.string.magnification_window_title));
+ params.accessibilityTitle = getAccessibilityWindowTitle();
mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view);
@@ -369,6 +379,10 @@
return regionInsideDragBorder;
}
+ private String getAccessibilityWindowTitle() {
+ return mResources.getString(com.android.internal.R.string.android_system_label);
+ }
+
private void showControls() {
if (mMirrorWindowControl != null) {
mMirrorWindowControl.showControl();
@@ -432,8 +446,8 @@
}
final int maxMirrorViewX = mDisplaySize.x - mMirrorView.getWidth();
final int maxMirrorViewY = mDisplaySize.y - mMirrorView.getHeight() - mNavGestureHeight;
- WindowManager.LayoutParams params =
- (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+ LayoutParams params =
+ (LayoutParams) mMirrorView.getLayoutParams();
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
// If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index bbabaf42..a59c876 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -22,7 +22,6 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
import com.android.wm.shell.pip.PipBoundsState;
@@ -107,10 +106,9 @@
PipBoundsHandler pipBoundsHandler,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
- PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
- SystemWindows systemWindows) {
+ PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows);
+ displayController, pipUiEventLogger, shellTaskOrganizer);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 1ca04af..0f8fb7b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -131,10 +131,9 @@
PipMenuActivityController menuActivityController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
- PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
- SystemWindows systemWindows) {
+ PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows);
+ displayController, pipUiEventLogger, shellTaskOrganizer);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index c923515..a0ae35f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -287,6 +287,33 @@
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
+ @Test
+ public void showButton_hasAccessibilityWindowTitle() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+ WindowManager.LayoutParams.class);
+ verify(mWindowManager).addView(eq(mSpyImageView), paramsArgumentCaptor.capture());
+ assertEquals(getContext().getResources().getString(
+ com.android.internal.R.string.android_system_label),
+ paramsArgumentCaptor.getValue().accessibilityTitle);
+ }
+
+ @Test
+ public void onLocaleChanged_buttonIsShowing_updateA11yWindowTitle() {
+ final String newA11yWindowTitle = "new a11y window title";
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ getContext().getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.android_system_label, newA11yWindowTitle);
+ mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+
+ ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+ WindowManager.LayoutParams.class);
+ verify(mWindowManager).updateViewLayout(eq(mSpyImageView), paramsArgumentCaptor.capture());
+ assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+ }
+
private void assertModeUnchanged(int expectedMode) {
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
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 f1606c5..a39bc70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -41,6 +41,7 @@
import android.content.res.Resources;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableResources;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -114,8 +115,7 @@
when(mTransaction.remove(any())).thenReturn(mTransaction);
when(mTransaction.setGeometry(any(), any(), any(),
anyInt())).thenReturn(mTransaction);
- mResources = Mockito.spy(mContext.getResources());
- when(mContext.getResources()).thenReturn(mResources);
+ mResources = getContext().getOrCreateTestableResources().getResources();
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
@@ -222,7 +222,6 @@
assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
}
-
@Test
public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
mInstrumentation.runOnMainSync(() -> {
@@ -310,4 +309,41 @@
verify(mWindowManager).updateViewLayout(eq(mMirrorView), any());
}
+
+ @Test
+ public void enableWindowMagnification_hasA11yWindowTitle() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+ WindowManager.LayoutParams.class);
+ verify(mWindowManager).addView(eq(mMirrorView), paramsArgumentCaptor.capture());
+ assertEquals(getContext().getResources().getString(
+ com.android.internal.R.string.android_system_label),
+ paramsArgumentCaptor.getValue().accessibilityTitle);
+ }
+
+ @Test
+ public void onLocaleChanged_enabled_updateA11yWindowTitle() {
+ final String newA11yWindowTitle = "new a11y window title";
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+ final TestableResources testableResources = getContext().getOrCreateTestableResources();
+ testableResources.addOverride(com.android.internal.R.string.android_system_label,
+ newA11yWindowTitle);
+ when(mContext.getResources()).thenReturn(testableResources.getResources());
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+ });
+
+ ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+ WindowManager.LayoutParams.class);
+ verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture());
+ assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+ }
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 60e59e3..28afcbb 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -85,6 +85,7 @@
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
+ DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cfc58a5..52fc93f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4306,7 +4306,7 @@
/** @see AudioManager#playSoundEffect(int, float) */
public void playSoundEffectVolume(int effectType, float volume) {
// do not try to play the sound effect if the system stream is muted
- if (isStreamMutedByRingerOrZenMode(STREAM_SYSTEM)) {
+ if (isStreamMute(STREAM_SYSTEM)) {
return;
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 88867fc..a4ae9c8 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -15,6 +15,7 @@
*/
package com.android.server.camera;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -39,6 +40,7 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
@@ -48,6 +50,8 @@
import com.android.server.SystemService.TargetUser;
import com.android.server.wm.WindowManagerInternal;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -79,10 +83,19 @@
// Handler message codes
private static final int MSG_SWITCH_USER = 1;
+ private static final int MSG_NOTIFY_DEVICE_STATE = 2;
private static final int RETRY_DELAY_TIME = 20; //ms
private static final int RETRY_TIMES = 60;
+ @IntDef(flag = true, prefix = { "DEVICE_STATE_" }, value = {
+ ICameraService.DEVICE_STATE_BACK_COVERED,
+ ICameraService.DEVICE_STATE_FRONT_COVERED,
+ ICameraService.DEVICE_STATE_FOLDED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DeviceStateFlags {}
+
// Maximum entries to keep in usage history before dumping out
private static final int MAX_USAGE_HISTORY = 100;
@@ -94,6 +107,15 @@
private final Object mLock = new Object();
private Set<Integer> mEnabledCameraUsers;
private int mLastUser;
+ // The current set of device state flags. May be different from mLastReportedDeviceState if the
+ // native camera service has not been notified of the change.
+ @GuardedBy("mLock")
+ @DeviceStateFlags
+ private int mDeviceState;
+ // The most recent device state flags reported to the native camera server.
+ @GuardedBy("mLock")
+ @DeviceStateFlags
+ private int mLastReportedDeviceState;
private ICameraService mCameraServiceRaw;
@@ -185,6 +207,7 @@
return;
}
notifySwitchWithRetries(RETRY_TIMES);
+ notifyDeviceStateWithRetries(RETRY_TIMES);
}
@Override
@@ -218,12 +241,55 @@
mLogWriterService.allowCoreThreadTimeOut(true);
}
+ /**
+ * Sets the device state bits set within {@code deviceStateFlags} leaving all other bits the
+ * same.
+ * <p>
+ * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}.
+ *
+ * @param deviceStateFlags a bitmask of the device state bits that should be set.
+ *
+ * @see #clearDeviceStateFlags(int)
+ */
+ public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
+ mDeviceState |= deviceStateFlags;
+ if (mDeviceState != mLastReportedDeviceState) {
+ notifyDeviceStateWithRetriesLocked(RETRY_TIMES);
+ }
+ }
+ }
+
+ /**
+ * Clears the device state bits set within {@code deviceStateFlags} leaving all other bits the
+ * same.
+ * <p>
+ * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}.
+ *
+ * @param deviceStateFlags a bitmask of the device state bits that should be cleared.
+ *
+ * @see #setDeviceStateFlags(int)
+ */
+ public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
+ mDeviceState &= ~deviceStateFlags;
+ if (mDeviceState != mLastReportedDeviceState) {
+ notifyDeviceStateWithRetriesLocked(RETRY_TIMES);
+ }
+ }
+ }
+
@Override
public boolean handleMessage(Message msg) {
switch(msg.what) {
case MSG_SWITCH_USER: {
notifySwitchWithRetries(msg.arg1);
} break;
+ case MSG_NOTIFY_DEVICE_STATE: {
+ notifyDeviceStateWithRetries(msg.arg1);
+ } break;
default: {
Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
} break;
@@ -386,6 +452,25 @@
}
}
+ @Nullable
+ private ICameraService getCameraServiceRawLocked() {
+ if (mCameraServiceRaw == null) {
+ IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+ if (cameraServiceBinder == null) {
+ return null;
+ }
+ try {
+ cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not link to death of native camera service");
+ return null;
+ }
+
+ mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+ }
+ return mCameraServiceRaw;
+ }
+
private void switchUserLocked(int userHandle) {
Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
mLastUser = userHandle;
@@ -431,20 +516,10 @@
private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) {
// Forward the user switch event to the native camera service running in the cameraserver
// process.
- if (mCameraServiceRaw == null) {
- IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
- if (cameraServiceBinder == null) {
- Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
- return false; // Camera service not active, cannot evict user clients.
- }
- try {
- cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "Could not link to death of native camera service");
- return false;
- }
-
- mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+ ICameraService cameraService = getCameraServiceRawLocked();
+ if (cameraService == null) {
+ Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
+ return false;
}
try {
@@ -457,6 +532,43 @@
return true;
}
+ private void notifyDeviceStateWithRetries(int retries) {
+ synchronized (mLock) {
+ notifyDeviceStateWithRetriesLocked(retries);
+ }
+ }
+
+ private void notifyDeviceStateWithRetriesLocked(int retries) {
+ if (notifyDeviceStateChangeLocked(mDeviceState)) {
+ return;
+ }
+ if (retries <= 0) {
+ return;
+ }
+ Slog.i(TAG, "Could not notify camera service of device state change, retrying...");
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_NOTIFY_DEVICE_STATE, retries - 1,
+ 0, null), RETRY_DELAY_TIME);
+ }
+
+ private boolean notifyDeviceStateChangeLocked(@DeviceStateFlags int deviceState) {
+ // Forward the state to the native camera service running in the cameraserver process.
+ ICameraService cameraService = getCameraServiceRawLocked();
+ if (cameraService == null) {
+ Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
+ return false;
+ }
+
+ try {
+ mCameraServiceRaw.notifyDeviceStateChange(deviceState);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
+ // Not much we can do if camera service is dead.
+ return false;
+ }
+ mLastReportedDeviceState = deviceState;
+ return true;
+ }
+
private void updateActivityCount(String cameraId, int newCameraState, int facing,
String clientName, int apiLevel) {
synchronized(mLock) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 7d76628..e656143 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -343,6 +343,78 @@
return true;
}
+ /**
+ * Check if the given value is a valid day of month. A valid value is one which falls within the
+ * range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value day of month
+ * @return true if the day of month is valid
+ */
+ private boolean isValidDayOfMonth(int value) {
+ return isWithinRange(value, 1, 31);
+ }
+
+ /**
+ * Check if the given value is a valid month of year. A valid value is one which falls within
+ * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value month of year
+ * @return true if the month of year is valid
+ */
+ private boolean isValidMonthOfYear(int value) {
+ return isWithinRange(value, 1, 12);
+ }
+
+ /**
+ * Check if the given value is a valid hour. A valid value is one which falls within the range
+ * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value hour
+ * @return true if the hour is valid
+ */
+ private boolean isValidHour(int value) {
+ return isWithinRange(value, 0, 23);
+ }
+
+ /**
+ * Check if the given value is a valid minute. A valid value is one which falls within the range
+ * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value minute
+ * @return true if the minute is valid
+ */
+ private boolean isValidMinute(int value) {
+ return isWithinRange(value, 0, 59);
+ }
+
+ /**
+ * Check if the given value is a valid duration hours. A valid value is one which falls within
+ * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value duration hours
+ * @return true if the duration hours is valid
+ */
+ private boolean isValidDurationHours(int value) {
+ return isWithinRange(value, 0, 99);
+ }
+
+ /**
+ * Check if the given value is a valid recording sequence. A valid value is adheres to range
+ * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value recording sequence
+ * @return true if the given recording sequence is valid
+ */
+ private boolean isValidRecordingSequence(int value) {
+ value = value & 0xFF;
+ // Validate bit 7 is set to zero
+ if ((value & 0x80) != 0x00) {
+ return false;
+ }
+ // Validate than not more than one bit is set
+ return (Integer.bitCount(value) <= 1);
+ }
+
private class PhysicalAddressValidator implements ParameterValidator {
@Override
public int isValid(byte[] params) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b532fa1..9a60afb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3254,9 +3254,6 @@
boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
- if (mCurClient == null || mCurClient.curSession == null) {
- return false;
- }
if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mShowExplicitlyRequested || mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b3f49ad..925d3cc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -545,6 +545,7 @@
static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
static final int SCAN_AS_ODM = 1 << 22;
static final int SCAN_AS_APK_IN_APEX = 1 << 23;
+ static final int SCAN_EXPECTED_BETTER = 1 << 24;
@IntDef(flag = true, prefix = { "SCAN_" }, value = {
SCAN_NO_DEX,
@@ -804,11 +805,12 @@
final SparseIntArray mIsolatedOwners = new SparseIntArray();
/**
- * Tracks new system packages [received in an OTA] that we expect to
- * find updated user-installed versions. Keys are package name, values
- * are package location.
+ * Tracks packages that we expect to find updated versions of on disk.
+ * Keys are package name, values are package location and package version code.
+ *
+ * @see #expectBetter(String, File, long)
*/
- final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+ private final ArrayMap<String, List<Pair<File, Long>>> mExpectingBetter = new ArrayMap<>();
/**
* Tracks existing packages prior to receiving an OTA. Keys are package name.
@@ -3349,7 +3351,7 @@
+ ", versionCode=" + ps.versionCode
+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());
removePackageLI(scannedPkg, true);
- mExpectingBetter.put(ps.name, ps.getPath());
+ expectBetter(ps.name, ps.getPath(), ps.versionCode);
}
continue;
@@ -3379,7 +3381,8 @@
// 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.getPath());
+ expectBetter(disabledPs.name, disabledPs.getPath(),
+ disabledPs.versionCode);
}
}
}
@@ -3480,38 +3483,48 @@
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
- final File scanFile = mExpectingBetter.valueAt(i);
-
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
- @ParseFlags int reparseFlags = 0;
- @ScanFlags int rescanFlags = 0;
- for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
- final ScanPartition partition = mDirsToScanAsSystem.get(i1);
- if (partition.containsPrivApp(scanFile)) {
- reparseFlags = systemParseFlags;
- rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
- | partition.scanFlag;
- break;
- }
- if (partition.containsApp(scanFile)) {
- reparseFlags = systemParseFlags;
- rescanFlags = systemScanFlags | partition.scanFlag;
- break;
- }
- }
- if (rescanFlags == 0) {
- Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
- continue;
- }
- mSettings.enableSystemPackageLPw(packageName);
+ final List<Pair<File, Long>> scanFiles = mExpectingBetter.valueAt(i);
+ // Sort ascending and iterate backwards to take highest version code
+ Collections.sort(scanFiles,
+ (first, second) -> Long.compare(first.second, second.second));
+ for (int index = scanFiles.size() - 1; index >= 0; index--) {
+ File scanFile = scanFiles.get(index).first;
- try {
- scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse original system package: "
- + e.getMessage());
+ @ParseFlags int reparseFlags = 0;
+ @ScanFlags int rescanFlags = 0;
+ for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+ final ScanPartition partition = mDirsToScanAsSystem.get(i1);
+ if (partition.containsPrivApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+ | partition.scanFlag;
+ break;
+ }
+ if (partition.containsApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | partition.scanFlag;
+ break;
+ }
+ }
+ if (rescanFlags == 0) {
+ Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
+ continue;
+ }
+ mSettings.enableSystemPackageLPw(packageName);
+
+ rescanFlags |= SCAN_EXPECTED_BETTER;
+
+ try {
+ scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
+ // Take first success and break out of for loop
+ break;
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse original system package: "
+ + e.getMessage());
+ }
}
}
}
@@ -3901,6 +3914,33 @@
}
/**
+ * Mark a package as skipped during initial scan, expecting a more up to date version to be
+ * available on the scan of a higher priority partition. This can be either a system partition
+ * or the data partition.
+ *
+ * If for some reason that newer version cannot be scanned successfully, the data structure
+ * created here will be used to backtrack in the scanning process to try and take the highest
+ * version code of the package left on disk that scans successfully.
+ *
+ * This can occur if an OTA adds a new system package which the user has already installed an
+ * update on data for. Or if the device image includes multiple versions of the same package,
+ * for cases where the maintainer of a higher priority partition wants to update an app on
+ * a lower priority partition before shipping a device to users.
+ *
+ * @param pkgName the package name identifier to queue under
+ * @param codePath the path to re-scan if needed
+ * @param knownVersionCode the version of the package so that the set of files can be sorted
+ */
+ private void expectBetter(String pkgName, File codePath, long knownVersionCode) {
+ List<Pair<File, Long>> pairs = mExpectingBetter.get(pkgName);
+ if (pairs == null) {
+ pairs = new ArrayList<>(0);
+ mExpectingBetter.put(pkgName, pairs);
+ }
+ pairs.add(Pair.create(codePath, knownVersionCode));
+ }
+
+ /**
* Extract, install and enable a stub package.
* <p>If the compressed file can not be extracted / installed for any reason, the stub
* APK will be installed and the package will be disabled. To recover from this situation,
@@ -11271,7 +11311,23 @@
isUpdatedSystemApp = disabledPkgSetting != null;
}
applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);
- assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
+ try {
+ assertPackageIsValid(parsedPackage, pkgSetting, parseFlags, scanFlags);
+ } catch (PackageManagerException e) {
+ if (e.error == INSTALL_FAILED_VERSION_DOWNGRADE
+ && ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0)
+ && ((scanFlags & SCAN_BOOTING) != 0)) {
+ if (pkgSetting != null && pkgSetting.getPkg() == null) {
+ // If a package for the pkgSetting hasn't already been found, this is
+ // skipping a downgrade on a lower priority partition, and so a later scan
+ // is expected to fill the package.
+ expectBetter(pkgSetting.name, new File(parsedPackage.getPath()),
+ parsedPackage.getLongVersionCode());
+ }
+ }
+
+ throw e;
+ }
SharedUserSetting sharedUserSetting = null;
if (parsedPackage.getSharedUserId() != null) {
@@ -12123,9 +12179,9 @@
*
* @throws PackageManagerException If the package fails any of the validation checks
*/
- private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags)
- throws PackageManagerException {
+ private void assertPackageIsValid(AndroidPackage pkg,
+ @Nullable PackageSetting existingPkgSetting, final @ParseFlags int parseFlags,
+ final @ScanFlags int scanFlags) throws PackageManagerException {
if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
assertCodePolicy(pkg);
}
@@ -12140,11 +12196,11 @@
// after OTA.
final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ String pkgName = pkg.getPackageName();
if ((isUserInstall || isFirstBootOrUpgrade)
- && mApexManager.isApexPackage(pkg.getPackageName())) {
+ && mApexManager.isApexPackage(pkgName)) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- pkg.getPackageName()
- + " is an APEX package and can't be installed as an APK.");
+ pkgName + " is an APEX package and can't be installed as an APK.");
}
// Make sure we're not adding any bogus keyset info
@@ -12153,7 +12209,7 @@
synchronized (mLock) {
// The special "android" package can only be defined once
- if (pkg.getPackageName().equals("android")) {
+ if (pkgName.equals("android")) {
if (mAndroidApplication != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
@@ -12164,12 +12220,46 @@
}
}
- // A package name must be unique; don't allow duplicates
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPackages.containsKey(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.getPackageName()
- + " already installed. Skipping duplicate.");
+ final long newLongVersionCode = pkg.getLongVersionCode();
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ boolean runDuplicateCheck = false;
+
+ // It's possible to re-scan a package if an updated system app was expected, but
+ // no update on /data could be found. To avoid infinitely looping, a flag is passed
+ // in when re-scanning and this first branch is skipped if the flag is set.
+ if ((scanFlags & SCAN_EXPECTED_BETTER) == 0 && existingPkgSetting != null) {
+ long existingLongVersionCode = existingPkgSetting.versionCode;
+ if (newLongVersionCode <= existingLongVersionCode) {
+ // Must check that real name is equivalent, as it's possible to downgrade
+ // version code if the package is actually a different package taking over
+ // a package name through <original-package/>. It is assumed that this
+ // migration is one time, one way, and that there is no failsafe if this
+ // doesn't hold true.
+ if (Objects.equals(existingPkgSetting.realName, pkg.getRealPackage())) {
+ if (newLongVersionCode != existingLongVersionCode) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Ignoring lower version " + newLongVersionCode
+ + " for package " + pkgName
+ + " with expected version "
+ + existingLongVersionCode);
+ }
+ }
+ } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0
+ && (scanFlags & SCAN_BOOTING) != 0) {
+ // During system boot scan, if there's already a package known, but this
+ // package is higher version, use it instead, ignoring the duplicate check.
+ // This will store the higher version in the setting object, and the above
+ // branch/exception will cause future scans to skip the lower versions.
+ runDuplicateCheck = false;
+ }
+ }
+
+ if (runDuplicateCheck && mPackages.containsKey(pkgName)) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkgName
+ + " already installed. Skipping duplicate.");
+ }
}
if (pkg.isStaticSharedLibrary()) {
@@ -12289,8 +12379,8 @@
}
}
}
- if (pkg.getLongVersionCode() < minVersionCode
- || pkg.getLongVersionCode() > maxVersionCode) {
+ if (newLongVersionCode < minVersionCode
+ || newLongVersionCode > maxVersionCode) {
throw new PackageManagerException("Static shared"
+ " lib version codes must be ordered as lib versions");
}
@@ -12305,11 +12395,10 @@
// to the user-installed location. If we don't allow this change, any newer,
// user-installed version of the application will be ignored.
if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mExpectingBetter.containsKey(pkg.getPackageName())) {
- Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
+ if (mExpectingBetter.containsKey(pkgName)) {
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkgName);
} else {
- PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
+ PackageSetting known = mSettings.getPackageLPr(pkgName);
if (known != null) {
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Examining " + pkg.getPath()
@@ -12317,14 +12406,14 @@
}
if (!pkg.getPath().equals(known.getPathString())) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.getPackageName()
+ "Application package " + pkgName
+ " found at " + pkg.getPath()
+ " but expected at " + known.getPathString()
+ "; ignoring.");
}
} else {
throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Application package " + pkg.getPackageName()
+ "Application package " + pkgName
+ " not found; ignoring.");
}
}
@@ -12347,7 +12436,7 @@
INSTALL_FAILED_PROCESS_NOT_DEFINED,
"Can't install because application tag's process attribute "
+ pkg.getProcessName()
- + " (in package " + pkg.getPackageName()
+ + " (in package " + pkgName
+ ") is not included in the <processes> list");
}
assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
@@ -12371,7 +12460,7 @@
pkg.getSigningDetails().signatures)) {
throw new PackageManagerException("Apps that share a user with a " +
"privileged app must themselves be marked as privileged. " +
- pkg.getPackageName() + " shares privileged user " +
+ pkgName + " shares privileged user " +
pkg.getSharedUserId() + ".");
}
}
@@ -12388,21 +12477,21 @@
// upgraded.
Objects.requireNonNull(mOverlayConfig,
"Parsing non-system dir before overlay configs are initialized");
- if (!mOverlayConfig.isMutable(pkg.getPackageName())) {
+ if (!mOverlayConfig.isMutable(pkgName)) {
throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
+ + pkgName
+ " is static and cannot be upgraded.");
}
} else {
if ((scanFlags & SCAN_AS_VENDOR) != 0) {
if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ Slog.w(TAG, "System overlay " + pkgName
+ " targets an SDK below the required SDK level of vendor"
+ " overlays (" + getVendorPartitionVersion() + ")."
+ " This will become an install error in a future release");
}
} else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ Slog.w(TAG, "System overlay " + pkgName
+ " targets an SDK below the required SDK level of system"
+ " overlays (" + Build.VERSION.SDK_INT + ")."
+ " This will become an install error in a future release");
@@ -12418,7 +12507,7 @@
if (!comparePackageSignatures(platformPkgSetting,
pkg.getSigningDetails().signatures)) {
throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
+ + pkgName
+ " must target Q or later, "
+ "or be signed with the platform certificate");
}
@@ -12440,7 +12529,7 @@
// check reference signature
if (mOverlayConfigSignaturePackage == null) {
throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " and target "
+ + pkgName + " and target "
+ pkg.getOverlayTarget() + " signed with"
+ " different certificates, and the overlay lacks"
+ " <overlay android:targetName>");
@@ -12450,7 +12539,7 @@
if (!comparePackageSignatures(refPkgSetting,
pkg.getSigningDetails().signatures)) {
throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " signed with a different "
+ + pkgName + " signed with a different "
+ "certificate than both the reference package and "
+ "target " + pkg.getOverlayTarget() + ", and the "
+ "overlay lacks <overlay android:targetName>");
@@ -12470,7 +12559,7 @@
if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
+ + " or newer for package " + pkgName);
}
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 74ec161..3463daf 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -57,7 +57,16 @@
* Returns the cache key for a specified {@code packageFile} and {@code flags}.
*/
private String getCacheKey(File packageFile, int flags) {
- StringBuilder sb = new StringBuilder(packageFile.getName());
+ StringBuilder sb = new StringBuilder();
+
+ // To support packages with the same file name across partitions, use the partition name
+ // as a prefix. The cache should only be used for cases where the file paths have been
+ // established using the unique partition names, without canonicalization, so any links
+ // which would point to the same partition name should be handled separately.
+ String cachePrefix = packageFile.toPath().getName(0).toString();
+ sb.append(cachePrefix);
+ sb.append('-');
+ sb.append(packageFile.getName());
sb.append('-');
sb.append(flags);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 851ddd1..46d31d9 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -135,7 +135,7 @@
}
/**
- * TODO(b/135203078): Document new package parsing
+ * TODO(b/155493909): Document new package parsing
*/
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index f8e26fc..b007a75 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -16,8 +16,10 @@
package com.android.server.policy;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.ICameraService;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -26,11 +28,13 @@
import android.os.Handler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDisplayFoldListener;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
+import com.android.server.camera.CameraServiceProxy;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -38,11 +42,13 @@
* TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
*/
class DisplayFoldController {
-
private static final String TAG = "DisplayFoldController";
private final WindowManagerInternal mWindowManagerInternal;
private final DisplayManagerInternal mDisplayManagerInternal;
+ // Camera service proxy can be disabled through a config.
+ @Nullable
+ private final CameraServiceProxy mCameraServiceProxy;
private final int mDisplayId;
private final Handler mHandler;
@@ -58,10 +64,12 @@
private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger();
DisplayFoldController(WindowManagerInternal windowManagerInternal,
- DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
+ DisplayManagerInternal displayManagerInternal,
+ @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea,
Handler handler) {
mWindowManagerInternal = windowManagerInternal;
mDisplayManagerInternal = displayManagerInternal;
+ mCameraServiceProxy = cameraServiceProxy;
mDisplayId = displayId;
mFoldedArea = new Rect(foldedArea);
mHandler = handler;
@@ -116,6 +124,16 @@
}
}
+ if (mCameraServiceProxy != null) {
+ if (folded) {
+ mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+ } else {
+ mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+ }
+ } else {
+ Slog.w(TAG, "Camera service unavailable to toggle folded state.");
+ }
+
mDurationLogger.setDeviceFolded(folded);
mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
mFolded = folded;
@@ -193,8 +211,13 @@
}
static DisplayFoldController create(Context context, int displayId) {
+ final WindowManagerInternal windowManagerService =
+ LocalServices.getService(WindowManagerInternal.class);
final DisplayManagerInternal displayService =
LocalServices.getService(DisplayManagerInternal.class);
+ final CameraServiceProxy cameraServiceProxy =
+ LocalServices.getService(CameraServiceProxy.class);
+
final String configFoldedArea = context.getResources().getString(
com.android.internal.R.string.config_foldedArea);
final Rect foldedArea;
@@ -204,7 +227,7 @@
foldedArea = Rect.unflattenFromString(configFoldedArea);
}
- return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class),
- displayService, displayId, foldedArea, DisplayThread.getHandler());
+ return new DisplayFoldController(windowManagerService, displayService, cameraServiceProxy,
+ displayId, foldedArea, DisplayThread.getHandler());
}
}
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index e72c185..f9ef994 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -36,6 +36,7 @@
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
@@ -216,6 +217,34 @@
failingPackageName,
new byte[]{});
}
+
+ logTestProperties(logPackage, type, rollbackReason, failingPackageName);
+ }
+
+ /**
+ * Writes properties which will be used by rollback tests to check if particular rollback
+ * events have occurred.
+ *
+ * persist.sys.rollbacktest.enabled: true if rollback tests are running
+ * persist.sys.rollbacktest.EVENT_TYPE: true if a particular rollback event has occurred
+ * ex: persist.sys.rollbacktest.ROLLBACK_INITIATE is true if ROLLBACK_INITIATE has happened
+ * persist.sys.rollbacktest.EVENT_TYPE.logPackage: the package to associate the rollback with
+ * persist.sys.rollbacktest.EVENT_TYPE.rollbackReason: the reason Watchdog triggered a rollback
+ * persist.sys.rollbacktest.EVENT_TYPE.failedPackageName: the failing package or process which
+ * triggered the rollback
+ */
+ private static void logTestProperties(@Nullable VersionedPackage logPackage, int type,
+ int rollbackReason, @NonNull String failingPackageName) {
+ // This property should be on only during the tests
+ final String prefix = "persist.sys.rollbacktest.";
+ if (!SystemProperties.getBoolean(prefix + "enabled", false)) {
+ return;
+ }
+ String key = prefix + rollbackTypeToString(type);
+ SystemProperties.set(key, String.valueOf(true));
+ SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : "");
+ SystemProperties.set(key + ".rollbackReason", rollbackReasonToString(rollbackReason));
+ SystemProperties.set(key + ".failedPackageName", failingPackageName);
}
@VisibleForTesting
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 03edc58..267c9b7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1266,6 +1266,12 @@
inputManager = new InputManagerService(context);
t.traceEnd();
+ if (!disableCameraService) {
+ t.traceBegin("StartCameraServiceProxy");
+ mSystemServiceManager.startService(CameraServiceProxy.class);
+ t.traceEnd();
+ }
+
t.traceBegin("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
@@ -2200,12 +2206,6 @@
t.traceEnd();
}
- if (!disableCameraService) {
- t.traceBegin("StartCameraServiceProxy");
- mSystemServiceManager.startService(CameraServiceProxy.class);
- t.traceEnd();
- }
-
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
t.traceBegin("StartIoTSystemService");
mSystemServiceManager.startService(IOT_SERVICE_CLASS);
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
index e17358d..7ae2fe0 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
@@ -17,11 +17,8 @@
class FactoryPackageTest : BaseHostJUnit4Test() {
companion object {
- 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 DEVICE_SIDE = "PackageManagerServiceDeviceSideTests.apk"
+ private const val DEVICE_SIDE_PKG_NAME = "com.android.server.pm.test.deviceside"
@get:ClassRule
val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -40,7 +37,8 @@
@Before
@After
fun removeApk() {
- device.uninstallPackage(TEST_PKG_NAME)
+ HostUtils.deleteAllTestPackages(device, preparer)
+ device.uninstallPackage(DEVICE_SIDE_PKG_NAME)
device.deleteFile(filePath.parent.toString())
device.reboot()
}
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 9399030..f0b60f4 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
@@ -23,6 +23,15 @@
import java.io.File
import java.io.FileOutputStream
+internal const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+internal const val VERSION_STUB = "PackageManagerTestAppStub.apk"
+internal const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+internal const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+internal const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
+internal const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
+internal const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
+internal const val VERSION_OVERRIDE = "PackageManagerTestAppOriginalOverride.apk"
+
internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
.toString())
@@ -89,12 +98,25 @@
internal object HostUtils {
- fun getDataDir(device: ITestDevice, pkgName: String) =
- device.executeShellCommand("dumpsys package $pkgName")
- .lineSequence()
- .map(String::trim)
- .single { it.startsWith("dataDir=") }
- .removePrefix("dataDir=")
+ /**
+ * Since most of the tests use the same test APKs, consolidate the logic for deleting them
+ * before and after a test runs. This also ensures that a failing test doesn't leave an APK on
+ * device that could spill over to another test when developing locally.
+ *
+ * Iterates all partitions since different tests use different partitions.
+ */
+ fun deleteAllTestPackages(device: ITestDevice, preparer: SystemPreparer) {
+ Partition.values().forEach { partition ->
+ device.uninstallPackage(TEST_PKG_NAME)
+ preparer.deleteApkFolders(partition, VERSION_ONE, VERSION_TWO, VERSION_THREE,
+ VERSION_THREE_INVALID, VERSION_FOUR, VERSION_OVERRIDE)
+ }
+
+ // TODO: There is an optimization that can be made here by hooking into the SystemPreparer's
+ // reboot rule, avoiding a reboot cycle by doing the delete in line the built in @After
+ // reboot.
+ preparer.reboot()
+ }
fun makePathForApk(fileName: String, partition: Partition) =
makePathForApk(File(fileName), partition)
@@ -136,14 +158,35 @@
}
.map(String::trim)
+ fun getDataDir(device: ITestDevice, pkgName: String) =
+ packageSection(device, pkgName)
+ .singleOrNull { it.startsWith("dataDir=") }
+ ?.removePrefix("dataDir=")
+
+ /** Return all code paths for a package. This will include hidden system package code paths. */
fun getCodePaths(device: ITestDevice, pkgName: String) =
- device.executeShellCommand("pm dump $pkgName")
- .lineSequence()
- .map(String::trim)
+ (packageSection(device, pkgName) +
+ packageSection(device, pkgName, "Hidden system packages"))
.filter { it.startsWith("codePath=") }
.map { it.removePrefix("codePath=") }
.toList()
+ fun getVersionCode(device: ITestDevice, pkgName: String) =
+ packageSection(device, pkgName)
+ .filter { it.startsWith("versionCode=") }
+ .map { it.removePrefix("versionCode=") }
+ .map { it.takeWhile { !it.isWhitespace() } }
+ .map { it.toInt() }
+ .firstOrNull()
+
+ fun getPrivateFlags(device: ITestDevice, pkgName: String) =
+ packageSection(device, pkgName)
+ .filter { it.startsWith("privateFlags=") }
+ .map { it.removePrefix("privateFlags=[ ") }
+ .map { it.removeSuffix(" ]") }
+ .map { it.split(" ") }
+ .firstOrNull()
+
private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
packageSection(device, pkgName)
.filter { it.startsWith("User ") }
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 37c999c..8594706 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,12 +33,6 @@
class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
companion object {
- 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)
}
@@ -55,7 +49,7 @@
@Before
@After
fun removeApk() {
- device.uninstallPackage(TEST_PKG_NAME)
+ HostUtils.deleteAllTestPackages(device, preparer)
preparer.deleteFile(filePath.parent.toString())
.reboot()
}
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 4becae6..0c5816b 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
@@ -34,10 +34,6 @@
companion object {
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)
@@ -54,9 +50,7 @@
@Before
@After
fun deleteApkFolders() {
- preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
- NEW_PKG)
- .reboot()
+ HostUtils.deleteAllTestPackages(device, preparer)
}
@Test
@@ -89,10 +83,10 @@
device.pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
preparer.deleteApkFolders(Partition.SYSTEM, apk)
- .pushApk(NEW_PKG, Partition.SYSTEM)
+ .pushApk(VERSION_OVERRIDE, Partition.SYSTEM)
.reboot()
- assertCodePath(NEW_PKG)
+ assertCodePath(VERSION_OVERRIDE)
// And then reading the data contents back
assertThat(device.pullFileContents(
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt
new file mode 100644
index 0000000..8d789e0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt
@@ -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.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+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
+
+/**
+ * Pushes APKs onto various system partitions to verify that multiple versions result in the
+ * highest version being scanned. Also tries to upgrade/replace these APKs which should result
+ * in a version upgrade on reboot.
+ *
+ * This will also verify that APKs under the same folder/file name across different partitions
+ * are parsed as separate entities and don't get combined under the same cache entry.
+ *
+ * Known limitations:
+ * - Does not verify that v1 isn't scanned. It's possible to introduce a bug that upgrades the
+ * system on every reboot from v1 -> v2, as this isn't easily visible after scan has finished.
+ * This would also have to successfully preserve the app data though, which seems unlikely.
+ * - This takes a very long time to run. 105 seconds for the first test case, up to 60 seconds for
+ * each following case. It's theoretically possible to parallelize these tests so that each
+ * method is run by installing all the apps under different names, requiring only 3 reboots to
+ * fully verify, rather than 3 * numTestCases.
+ */
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SystemAppScanPriorityTest : BaseHostJUnit4Test() {
+
+ companion object {
+ @get:ClassRule
+ var deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+ }
+
+ private val tempFolder = TemporaryFolder()
+ private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+ SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
+
+ private var firstReboot = true
+
+ @Rule
+ @JvmField
+ val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+
+ @Before
+ @After
+ fun deleteFiles() {
+ HostUtils.deleteAllTestPackages(device, preparer)
+ }
+
+ @Before
+ fun resetFirstReboot() {
+ firstReboot = true
+ }
+
+ @Test
+ fun takeHigherPriority() {
+ preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+ .pushFile(VERSION_TWO, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.PRODUCT)
+ }
+
+ @Test
+ fun takeLowerPriority() {
+ preparer.pushFile(VERSION_TWO, Partition.VENDOR)
+ .pushFile(VERSION_ONE, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.VENDOR)
+ }
+
+ @Test
+ fun upgradeToHigherOnLowerPriority() {
+ preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+ .pushFile(VERSION_TWO, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.PRODUCT)
+
+ preparer.pushFile(VERSION_THREE, Partition.VENDOR)
+ .rebootForTest()
+
+ assertVersionAndPartition(3, Partition.VENDOR)
+ }
+
+ @Test
+ fun upgradeToNewerOnHigherPriority() {
+ preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+ .pushFile(VERSION_TWO, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.PRODUCT)
+
+ preparer.pushFile(VERSION_THREE, Partition.SYSTEM_EXT)
+ .rebootForTest()
+
+ assertVersionAndPartition(3, Partition.SYSTEM_EXT)
+ }
+
+ @Test
+ fun replaceNewerOnLowerPriority() {
+ preparer.pushFile(VERSION_TWO, Partition.VENDOR)
+ .pushFile(VERSION_ONE, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.VENDOR)
+
+ preparer.pushFile(VERSION_THREE, Partition.VENDOR)
+ .rebootForTest()
+
+ assertVersionAndPartition(3, Partition.VENDOR)
+ }
+
+ @Test
+ fun replaceNewerOnHigherPriority() {
+ preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+ .pushFile(VERSION_TWO, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.PRODUCT)
+
+ preparer.pushFile(VERSION_THREE, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(3, Partition.PRODUCT)
+ }
+
+ @Test
+ fun fallbackToLowerPriority() {
+ preparer.pushFile(VERSION_TWO, Partition.VENDOR)
+ .pushFile(VERSION_ONE, Partition.PRODUCT)
+ .pushFile(VERSION_THREE, Partition.SYSTEM_EXT)
+ .rebootForTest()
+
+ assertVersionAndPartition(3, Partition.SYSTEM_EXT)
+
+ preparer.deleteFile(VERSION_THREE, Partition.SYSTEM_EXT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.VENDOR)
+ }
+
+ @Test
+ fun fallbackToHigherPriority() {
+ preparer.pushFile(VERSION_THREE, Partition.VENDOR)
+ .pushFile(VERSION_ONE, Partition.PRODUCT)
+ .pushFile(VERSION_TWO, Partition.SYSTEM_EXT)
+ .rebootForTest()
+
+ assertVersionAndPartition(3, Partition.VENDOR)
+
+ preparer.deleteFile(VERSION_THREE, Partition.VENDOR)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.SYSTEM_EXT)
+ }
+
+ @Test
+ fun removeBoth() {
+ preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+ .pushFile(VERSION_TWO, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertVersionAndPartition(2, Partition.PRODUCT)
+
+ preparer.deleteFile(VERSION_ONE, Partition.VENDOR)
+ .deleteFile(VERSION_TWO, Partition.PRODUCT)
+ .rebootForTest()
+
+ assertThat(device.getAppPackageInfo(TEST_PKG_NAME)).isNull()
+ }
+
+ private fun assertVersionAndPartition(versionCode: Int, partition: Partition) {
+ assertThat(HostUtils.getVersionCode(device, TEST_PKG_NAME)).isEqualTo(versionCode)
+
+ val privateFlags = HostUtils.getPrivateFlags(device, TEST_PKG_NAME)
+
+ when (partition) {
+ Partition.SYSTEM,
+ Partition.SYSTEM_PRIVILEGED -> {
+ assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString())
+ assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString())
+ assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString())
+ }
+ Partition.VENDOR -> {
+ assertThat(privateFlags).contains(Partition.VENDOR.toString())
+ assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString())
+ assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString())
+ }
+ Partition.PRODUCT -> {
+ assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString())
+ assertThat(privateFlags).contains(Partition.PRODUCT.toString())
+ assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString())
+ }
+ Partition.SYSTEM_EXT -> {
+ assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString())
+ assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString())
+ assertThat(privateFlags).contains(Partition.SYSTEM_EXT.toString())
+ }
+ }.run { /* exhaust */ }
+ }
+
+ // Following methods don't use HostUtils in order to test cache behavior when using the same
+ // name across partitions. Writes all files under the version 1 name.
+ private fun makeDevicePath(partition: Partition) =
+ partition.baseAppFolder
+ .resolve(File(VERSION_ONE).nameWithoutExtension)
+ .resolve(VERSION_ONE)
+ .toString()
+
+ private fun SystemPreparer.pushFile(file: String, partition: Partition) =
+ pushResourceFile(file, makeDevicePath(partition))
+
+ private fun SystemPreparer.deleteFile(file: String, partition: Partition) =
+ deleteFile(makeDevicePath(partition))
+
+ /**
+ * Custom reboot used to write app data after the first reboot. This can then be verified
+ * after each subsequent reboot to ensure no data is lost.
+ */
+ private fun SystemPreparer.rebootForTest() {
+ if (firstReboot) {
+ firstReboot = false
+ preparer.reboot()
+
+ val file = tempFolder.newFile()
+ file.writeText("Test")
+ pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
+ } else {
+ val versionBefore = HostUtils.getVersionCode(device, TEST_PKG_NAME)
+ preparer.reboot()
+ val versionAfter = HostUtils.getVersionCode(device, TEST_PKG_NAME)
+
+ if (versionBefore != null && versionAfter != null) {
+ val fileContents = device.pullFileContents(
+ "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
+ if (versionAfter < versionBefore) {
+ // A downgrade will wipe app data
+ assertThat(fileContents).isNull()
+ } else {
+ // An upgrade or update will preserve app data
+ assertThat(fileContents).isEqualTo("Test")
+ }
+ }
+ }
+ }
+}
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
index 46120af..99dff08 100644
--- 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
@@ -37,9 +37,6 @@
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
@@ -90,6 +87,12 @@
savedDevice?.removeUser(it)
}
+ savedDevice?.let { device ->
+ savedPreparer?.let { preparer ->
+ HostUtils.deleteAllTestPackages(device, preparer)
+ }
+ }
+
savedDevice?.uninstallPackage(TEST_PKG_NAME)
savedDevice?.deleteFile(stubFile.parent.toString())
savedDevice?.deleteFile(deviceCompressedFile.parent.toString())
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c982f49..425addc 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12015,12 +12015,13 @@
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return (long) telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
+ return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_BITMASK_UNKNOWN;
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index b7d72db..c769a6f 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -16,8 +16,6 @@
package com.android.tests.rollback.host;
-import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -31,7 +29,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -98,12 +95,11 @@
// Verify rollback was executed after health check deadline
runPhase("testNetworkFailedRollback_Phase3");
- List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
REASON_EXPLICIT_HEALTH_CHECK, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
null, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
} finally {
// Reconnect internet again so we won't break tests which assume internet available
getDevice().executeShellCommand("svc wifi enable");
@@ -134,8 +130,7 @@
// Verify rollback was not executed after health check deadline
runPhase("testNetworkPassedDoesNotRollback_Phase3");
- List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
- assertEquals(watchdogEventOccurred(watchdogEvents, null, null,
+ assertEquals(mLogger.watchdogEventOccurred(null, null,
REASON_EXPLICIT_HEALTH_CHECK, null), false);
}
}
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 725bfa9..2ebb9c1 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -16,8 +16,6 @@
package com.android.tests.rollback.host;
-import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -158,12 +156,11 @@
runPhase("testBadApkOnly_Phase4");
- List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
REASON_APP_CRASH, TESTAPP_A));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
null, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
}
@Test
@@ -192,12 +189,11 @@
// verify rollback committed
runPhase("testNativeWatchdogTriggersRollback_Phase3");
- List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
REASON_NATIVE_CRASH, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
null, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
}
@Test
@@ -233,12 +229,11 @@
// verify all available rollbacks have been committed
runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4");
- List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
REASON_NATIVE_CRASH, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
null, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
}
/**
@@ -316,12 +311,11 @@
// Verify rollback occurred due to crash of apk-in-apex
runPhase("testRollbackApexWithApkCrashing_Phase3");
- List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
REASON_APP_CRASH, TESTAPP_A));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
null, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+ assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
}
/**
diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
index 8873150..6b0d1f8 100644
--- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
+++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
@@ -17,52 +17,50 @@
package com.android.tests.rollback.host;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.LogcatReceiver;
-import com.android.tradefed.result.InputStreamSource;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
+import static com.google.common.truth.Truth.assertThat;
public class WatchdogEventLogger {
- private LogcatReceiver mReceiver;
+ private static final String[] ROLLBACK_EVENT_TYPES = {
+ "ROLLBACK_INITIATE", "ROLLBACK_BOOT_TRIGGERED", "ROLLBACK_SUCCESS"};
+ private static final String[] ROLLBACK_EVENT_ATTRS = {
+ "logPackage", "rollbackReason", "failedPackageName"};
+ private static final String PROP_PREFIX = "persist.sys.rollbacktest.";
- public void start(ITestDevice device) {
- mReceiver = new LogcatReceiver(device, "logcat -s WatchdogRollbackLogger",
- device.getOptions().getMaxLogcatDataSize(), 0);
- mReceiver.start();
- }
+ private ITestDevice mDevice;
- public void stop() {
- if (mReceiver != null) {
- mReceiver.stop();
- mReceiver.clear();
- }
- }
-
- /**
- * Returns a list of all Watchdog logging events which have occurred.
- */
- public List<String> getWatchdogLoggingEvents() throws Exception {
- try (InputStreamSource logcatStream = mReceiver.getLogcatData()) {
- return getWatchdogLoggingEvents(logcatStream);
- }
- }
-
- private static List<String> getWatchdogLoggingEvents(InputStreamSource inputStreamSource)
- throws Exception {
- List<String> watchdogEvents = new ArrayList<>();
- InputStream inputStream = inputStreamSource.createInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
- String line;
- while ((line = reader.readLine()) != null) {
- if (line.contains("Watchdog event occurred")) {
- watchdogEvents.add(line);
+ private void resetProperties(boolean enabled) throws Exception {
+ try {
+ mDevice.enableAdbRoot();
+ assertThat(mDevice.setProperty(
+ PROP_PREFIX + "enabled", String.valueOf(enabled))).isTrue();
+ for (String type : ROLLBACK_EVENT_TYPES) {
+ String key = PROP_PREFIX + type;
+ assertThat(mDevice.setProperty(key, "")).isTrue();
+ for (String attr : ROLLBACK_EVENT_ATTRS) {
+ assertThat(mDevice.setProperty(key + "." + attr, "")).isTrue();
+ }
}
+ } finally {
+ mDevice.disableAdbRoot();
}
- return watchdogEvents;
+ }
+
+ public void start(ITestDevice device) throws Exception {
+ mDevice = device;
+ resetProperties(true);
+ }
+
+ public void stop() throws Exception {
+ if (mDevice != null) {
+ resetProperties(false);
+ }
+ }
+
+ private boolean matchProperty(String type, String attr, String expectedVal) throws Exception {
+ String key = PROP_PREFIX + type + "." + attr;
+ String val = mDevice.getProperty(key);
+ return expectedVal == null || expectedVal.equals(val);
}
/**
@@ -71,33 +69,11 @@
* Check the value of all non-null parameters against the list of Watchdog events that have
* occurred, and return {@code true} if an event exists which matches all criteria.
*/
- public static boolean watchdogEventOccurred(List<String> loggingEvents,
- String type, String logPackage,
+ public boolean watchdogEventOccurred(String type, String logPackage,
String rollbackReason, String failedPackageName) throws Exception {
- List<String> eventCriteria = new ArrayList<>();
- if (type != null) {
- eventCriteria.add("type: " + type);
- }
- if (logPackage != null) {
- eventCriteria.add("logPackage: " + logPackage);
- }
- if (rollbackReason != null) {
- eventCriteria.add("rollbackReason: " + rollbackReason);
- }
- if (failedPackageName != null) {
- eventCriteria.add("failedPackageName: " + failedPackageName);
- }
- for (String loggingEvent: loggingEvents) {
- boolean matchesCriteria = true;
- for (String criterion: eventCriteria) {
- if (!loggingEvent.contains(criterion)) {
- matchesCriteria = false;
- }
- }
- if (matchesCriteria) {
- return true;
- }
- }
- return false;
+ return mDevice.getBooleanProperty(PROP_PREFIX + type, false)
+ && matchProperty(type, "logPackage", logPackage)
+ && matchProperty(type, "rollbackReason", rollbackReason)
+ && matchProperty(type, "failedPackageName", failedPackageName);
}
}