Merge "[pm] reject installs of apks with renderscript on 64-bit only devices" into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 62a3c38..5a24251 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19869,6 +19869,10 @@
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
+ field public static final int ENCODING_MPEGH_BL_L3 = 23; // 0x17
+ field public static final int ENCODING_MPEGH_BL_L4 = 24; // 0x18
+ field public static final int ENCODING_MPEGH_LC_L3 = 25; // 0x19
+ field public static final int ENCODING_MPEGH_LC_L4 = 26; // 0x1a
field public static final int ENCODING_OPUS = 20; // 0x14
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_24BIT_PACKED = 21; // 0x15
@@ -53106,10 +53110,12 @@
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int);
+ method @Deprecated @Nullable public String getTimeZone();
method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon);
method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon);
method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon);
method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon);
+ method @Deprecated public void setTimeZone(@Nullable String);
}
public class ArrayAdapter<T> extends android.widget.BaseAdapter implements android.widget.Filterable android.widget.ThemedSpinnerAdapter {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2c2443f..9bff793 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9604,29 +9604,6 @@
}
-package android.service.attestation {
-
- public abstract class ImpressionAttestationService extends android.app.Service {
- ctor public ImpressionAttestationService();
- method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
- method public abstract boolean onVerifyImpressionToken(@NonNull byte[], @NonNull android.service.attestation.ImpressionToken);
- }
-
- public final class ImpressionToken implements android.os.Parcelable {
- ctor public ImpressionToken(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
- method public int describeContents();
- method @NonNull public android.graphics.Rect getBoundsInWindow();
- method @NonNull public String getHashingAlgorithm();
- method @NonNull public byte[] getHmac();
- method @NonNull public byte[] getImageHash();
- method public long getScreenshotTimeMillis();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.attestation.ImpressionToken> CREATOR;
- }
-
-}
-
package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
@@ -10192,6 +10169,30 @@
}
+package android.service.screenshot {
+
+ public final class ScreenshotHash implements android.os.Parcelable {
+ ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
+ method public int describeContents();
+ method @NonNull public android.graphics.Rect getBoundsInWindow();
+ method @NonNull public String getHashingAlgorithm();
+ method @NonNull public byte[] getHmac();
+ method @NonNull public byte[] getImageHash();
+ method public long getScreenshotTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR;
+ }
+
+ public abstract class ScreenshotHasherService extends android.app.Service {
+ ctor public ScreenshotHasherService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
+ method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash);
+ field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService";
+ }
+
+}
+
package android.service.search {
public abstract class SearchUiService extends android.app.Service {
@@ -11806,7 +11807,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method public int setNrDualConnectivityState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 71d2177..4cd62eb 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -216,16 +216,6 @@
}
/**
- * Sets the total amount of power consumed since BatteryStats reset, mAh.
- */
- @SuppressWarnings("unchecked")
- @NonNull
- public T setConsumedPower(double consumedPower) {
- mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
- return (T) this;
- }
-
- /**
* Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the time component, e.g.
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 18dca68..f681c4f 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -36,11 +36,15 @@
private final int mModeledPowerComponentOffset;
PowerComponents(@NonNull Builder builder) {
- mTotalPowerConsumed = builder.mTotalPowerConsumed;
mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
mModeledPowerComponentOffset = builder.mModeledPowerComponentOffset;
mPowerComponents = builder.mPowerComponents;
mTimeComponents = builder.mTimeComponents;
+ double totalPower = 0;
+ for (int i = mPowerComponents.length - 1; i >= 0; i--) {
+ totalPower += mPowerComponents[i];
+ }
+ mTotalPowerConsumed = totalPower;
}
PowerComponents(@NonNull Parcel source) {
@@ -158,7 +162,6 @@
* Builder for PowerComponents.
*/
static final class Builder {
- private double mTotalPowerConsumed;
private final double[] mPowerComponents;
private final int mCustomPowerComponentCount;
private final long[] mTimeComponents;
@@ -181,15 +184,6 @@
}
/**
- * Sets the sum amount of power consumed since BatteryStats reset.
- */
- @NonNull
- public Builder setTotalPowerConsumed(double totalPowerConsumed) {
- mTotalPowerConsumed = totalPowerConsumed;
- return this;
- }
-
- /**
* Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
deleted file mode 100644
index 968d533..0000000
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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 android.service.attestation;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteCallback;
-
-/**
- * A service that handles generating and verify ImpressionTokens.
- *
- * The service will generate an ImpressionToken based on arguments passed in. Then later that same
- * ImpressionToken can be verified to determine that it was created by the system.
- *
- * @hide
- */
-@SystemApi
-public abstract class ImpressionAttestationService extends Service {
- /** @hide **/
- public static final String EXTRA_IMPRESSION_TOKEN =
- "android.service.attestation.extra.IMPRESSION_TOKEN";
-
- /** @hide **/
- public static final String EXTRA_VERIFICATION_STATUS =
- "android.service.attestation.extra.VERIFICATION_STATUS";
-
- /**
- * Manifest metadata key for the resource string array containing the names of all impression
- * attestation algorithms provided by the service.
- *
- * @hide
- */
- public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
- "android.attestation.available_algorithms";
-
- /**
- * The {@link Intent} action that must be declared as handled by a service in its manifest
- * for the system to recognize it as an impression attestation providing service.
- *
- * @hide
- */
- public static final String SERVICE_INTERFACE =
- "android.service.attestation.ImpressionAttestationService";
-
- private ImpressionAttestationServiceWrapper mWrapper;
- private Handler mHandler;
-
- public ImpressionAttestationService() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mWrapper = new ImpressionAttestationServiceWrapper();
- mHandler = new Handler(Looper.getMainLooper(), null, true);
- }
-
- @NonNull
- @Override
- public final IBinder onBind(@NonNull Intent intent) {
- return mWrapper;
- }
-
- /**
- * Generates the impression token that can be used to validate that the system
- * generated the token.
- *
- * @param salt The salt to use when generating the hmac. This should be unique to the
- * caller so the token cannot be verified by any other process.
- * @param screenshot The screenshot buffer for the content to attest.
- * @param bounds The size and position of the content being attested in the window.
- * @param hashAlgorithm The String for the hashing algorithm to use based values in
- * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
- * @return An impression token that can be used to validate information about the content.
- * Returns null when the arguments sent are invalid.
- */
- @Nullable
- public abstract ImpressionToken onGenerateImpressionToken(@NonNull byte[] salt,
- @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
- @NonNull String hashAlgorithm);
-
- /**
- * Call to verify that the impressionToken passed in was generated by the system.
- *
- * @param salt The salt value to use when verifying the hmac. This should be the
- * same value that was passed to
- * {@link #onGenerateImpressionToken(byte[],
- * HardwareBuffer, Rect, String)} to
- * generate the token.
- * @param impressionToken The token to verify that it was generated by the system.
- * @return true if the token can be verified that it was generated by the system.
- */
- public abstract boolean onVerifyImpressionToken(@NonNull byte[] salt,
- @NonNull ImpressionToken impressionToken);
-
- private void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds,
- hashAlgorithm);
- final Bundle data = new Bundle();
- data.putParcelable(EXTRA_IMPRESSION_TOKEN, impressionToken);
- callback.sendResult(data);
- }
-
- private void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
- RemoteCallback callback) {
- boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken);
- final Bundle data = new Bundle();
- data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
- callback.sendResult(data);
- }
-
- private final class ImpressionAttestationServiceWrapper extends
- IImpressionAttestationService.Stub {
- @Override
- public void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- mHandler.sendMessage(
- obtainMessage(ImpressionAttestationService::generateImpressionToken,
- ImpressionAttestationService.this, salt, screenshot, bounds,
- hashAlgorithm, callback));
- }
-
- @Override
- public void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
- RemoteCallback callback) {
- mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken,
- ImpressionAttestationService.this, salt, impressionToken, callback));
- }
- }
-}
diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS
deleted file mode 100644
index b9e7b99..0000000
--- a/core/java/android/service/attestation/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chaviw@google.com
-ogunwale@google.com
diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
similarity index 66%
rename from core/java/android/service/attestation/IImpressionAttestationService.aidl
rename to core/java/android/service/screenshot/IScreenshotHasherService.aidl
index 5ff8f17..d14d147 100644
--- a/core/java/android/service/attestation/IImpressionAttestationService.aidl
+++ b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
@@ -14,43 +14,43 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
/**
- * Service used to handle impression attestation requests.
+ * Service used to handle ScreenshotHash requests.
*
* @hide
*/
-oneway interface IImpressionAttestationService {
+oneway interface IScreenshotHasherService {
/**
- * Generates the impression token that can be used to validate that the system generated the
+ * Generates the ScreenshotHash token that can be used to validate that the system generated the
* token.
*
* @param salt The salt to use when generating the hmac. This should be unique to the caller so
* the token cannot be verified by any other process.
* @param screenshot The screenshot to generate the hash and add to the token.
- * @param bounds The size and position of the content being attested in the window.
+ * @param bounds The size and position of the content being screenshot in the window.
* @param hashAlgorithm The String for the hashing algorithm to use based on values in
* {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
- * @param Callback The callback invoked to send back the impression token.
+ * @param Callback The callback invoked to send back the ScreenshotHash token.
*/
- void generateImpressionToken(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
+ void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
in String hashAlgorithm, in RemoteCallback callback);
/**
- * Call to verify that the impressionToken passed in was generated by the system. The result
+ * Call to verify that the ScreenshotHash passed in was generated by the system. The result
* will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
*
* @param salt The salt value to use when verifying the hmac. This should be the same value that
- * was passed to {@link generateImpressionToken()} to generate the token.
- * @param impressionToken The token to verify that it was generated by the system.
+ * was passed to {@link generateScreenshotHash()} to generate the token.
+ * @param screenshotHash The hash to verify that it was generated by the system.
* @param callback The callback invoked to send back the verification status.
*/
- void verifyImpressionToken(in byte[] salt, in ImpressionToken impressionToken,
+ void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash,
in RemoteCallback callback);
}
diff --git a/core/java/android/service/screenshot/OWNERS b/core/java/android/service/screenshot/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/java/android/service/screenshot/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/service/screenshot/ScreenshotHash.aidl
similarity index 90%
rename from core/java/android/service/attestation/ImpressionToken.aidl
rename to core/java/android/service/screenshot/ScreenshotHash.aidl
index 284a4ba..a7c50db 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/service/screenshot/ScreenshotHash.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable ScreenshotHash;
diff --git a/core/java/android/service/attestation/ImpressionToken.java b/core/java/android/service/screenshot/ScreenshotHash.java
similarity index 81%
rename from core/java/android/service/attestation/ImpressionToken.java
rename to core/java/android/service/screenshot/ScreenshotHash.java
index 4355dc0..9ae4192 100644
--- a/core/java/android/service/attestation/ImpressionToken.java
+++ b/core/java/android/service/screenshot/ScreenshotHash.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,14 +25,14 @@
import com.android.internal.util.DataClass;
/**
- * The ads impression token used to validate information about what was present on screen.
+ * The screenshot hash used to validate information about what was present on screen.
* @hide
*
* TODO: Remove hide and SystemAPI since this will be a public class
*/
@SystemApi
@DataClass(genToString = true, genAidl = true)
-public final class ImpressionToken implements Parcelable {
+public final class ScreenshotHash implements Parcelable {
/**
* The timestamp when the screenshot was generated.
*/
@@ -42,23 +42,27 @@
* The bounds of the requested area to take the screenshot. This is in window space passed in
* by the client.
*/
- private @NonNull final Rect mBoundsInWindow;
+ @NonNull
+ private final Rect mBoundsInWindow;
/**
* The selected hashing algorithm that generated the image hash.
*/
- private @NonNull final String mHashingAlgorithm;
+ @NonNull
+ private final String mHashingAlgorithm;
/**
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
- private @NonNull final byte[] mImageHash;
+ @NonNull
+ private final byte[] mImageHash;
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
- private @NonNull final byte[] mHmac;
+ @NonNull
+ private final byte[] mHmac;
/**
* The hmac generated by the system and used to verify whether this token was generated by
@@ -67,19 +71,21 @@
* @hide
*/
@SystemApi
- public @NonNull byte[] getHmac() {
+ @NonNull
+ public byte[] getHmac() {
return mHmac;
}
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/attestation/ImpressionToken.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/screenshot
+ // /ScreenshotHash.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -87,7 +93,7 @@
/**
- * Creates a new ImpressionToken.
+ * Creates a new ScreenshotHash.
*
* @param screenshotTimeMillis
* The timestamp when the screenshot was generated.
@@ -97,13 +103,13 @@
* @param hashingAlgorithm
* The selected hashing algorithm that generated the image hash.
* @param imageHash
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
* @param hmac
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
@DataClass.Generated.Member
- public ImpressionToken(
+ public ScreenshotHash(
long screenshotTimeMillis,
@NonNull Rect boundsInWindow,
@NonNull String hashingAlgorithm,
@@ -152,7 +158,7 @@
}
/**
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
@DataClass.Generated.Member
public @NonNull byte[] getImageHash() {
@@ -165,7 +171,7 @@
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
- return "ImpressionToken { " +
+ return "ScreenshotHash { " +
"screenshotTimeMillis = " + mScreenshotTimeMillis + ", " +
"boundsInWindow = " + mBoundsInWindow + ", " +
"hashingAlgorithm = " + mHashingAlgorithm + ", " +
@@ -194,7 +200,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ ImpressionToken(@NonNull Parcel in) {
+ /* package-private */ ScreenshotHash(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -222,24 +228,24 @@
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ImpressionToken> CREATOR
- = new Parcelable.Creator<ImpressionToken>() {
+ public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR
+ = new Parcelable.Creator<ScreenshotHash>() {
@Override
- public ImpressionToken[] newArray(int size) {
- return new ImpressionToken[size];
+ public ScreenshotHash[] newArray(int size) {
+ return new ScreenshotHash[size];
}
@Override
- public ImpressionToken createFromParcel(@NonNull Parcel in) {
- return new ImpressionToken(in);
+ public ScreenshotHash createFromParcel(@NonNull Parcel in) {
+ return new ScreenshotHash(in);
}
};
@DataClass.Generated(
- time = 1604539951959L,
- codegenVersion = "1.0.18",
- sourceFile = "frameworks/base/core/java/android/service/attestation/ImpressionToken.java",
- inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ImpressionToken extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
+ time = 1612383172822L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java",
+ inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/screenshot/ScreenshotHasherService.java b/core/java/android/service/screenshot/ScreenshotHasherService.java
new file mode 100644
index 0000000..d96cc7e
--- /dev/null
+++ b/core/java/android/service/screenshot/ScreenshotHasherService.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 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 android.service.screenshot;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+
+/**
+ * A service that handles generating and verify {@link ScreenshotHash}.
+ *
+ * The service will generate a ScreenshotHash based on arguments passed in. Then later that
+ * same ScreenshotHash can be verified to determine that it was created by the system.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ScreenshotHasherService extends Service {
+ /** @hide **/
+ public static final String EXTRA_SCREENSHOT_HASH =
+ "android.service.screenshot.extra.SCREENSHOT_HASH";
+
+ /** @hide **/
+ public static final String EXTRA_VERIFICATION_STATUS =
+ "android.service.screenshot.extra.VERIFICATION_STATUS";
+
+ /**
+ * Manifest metadata key for the resource string array containing the names of all hashing
+ * algorithms provided by the service.
+ *
+ * @hide
+ */
+ public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+ "android.screenshot.available_algorithms";
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service in its manifest
+ * for the system to recognize it as a ScreenshotHash providing service.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_INTERFACE =
+ "android.service.screenshot.ScreenshotHasherService";
+
+ private ScreenshotHasherServiceWrapper mWrapper;
+ private Handler mHandler;
+
+ public ScreenshotHasherService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new ScreenshotHasherServiceWrapper();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @NonNull
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Generates the ScreenshotHash that can be used to validate that the system generated the
+ * token.
+ *
+ * @param salt The salt to use when generating the hmac. This should be unique to the
+ * caller so the token cannot be verified by any other process.
+ * @param screenshot The screenshot buffer for the content.
+ * @param bounds The size and position of the content being screenshot in the window.
+ * @param hashAlgorithm The String for the hashing algorithm to use based values in
+ * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+ * @return A ScreenshotHash that can be used to validate information about the content.
+ * Returns null when the arguments sent are invalid.
+ */
+ @Nullable
+ public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt,
+ @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
+ @NonNull String hashAlgorithm);
+
+ /**
+ * Call to verify that the ScreenshotHash passed in was generated by the system.
+ *
+ * @param salt The salt value to use when verifying the hmac. This should be the
+ * same value that was passed to
+ * {@link #onGenerateScreenshotHash(byte[],
+ * HardwareBuffer, Rect, String)} to
+ * generate the token.
+ * @param screenshotHash The token to verify that it was generated by the system.
+ * @return true if the token can be verified that it was generated by the system.
+ */
+ public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt,
+ @NonNull ScreenshotHash screenshotHash);
+
+ private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot,
+ bounds,
+ hashAlgorithm);
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash);
+ callback.sendResult(data);
+ }
+
+ private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+ RemoteCallback callback) {
+ boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash);
+ final Bundle data = new Bundle();
+ data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
+ callback.sendResult(data);
+ }
+
+ private final class ScreenshotHasherServiceWrapper extends
+ IScreenshotHasherService.Stub {
+ @Override
+ public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(ScreenshotHasherService::generateScreenshotHash,
+ ScreenshotHasherService.this, salt, screenshot, bounds,
+ hashAlgorithm, callback));
+ }
+
+ @Override
+ public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+ RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(ScreenshotHasherService::verifyScreenshotHash,
+ ScreenshotHasherService.this, salt, screenshotHash,
+ callback));
+ }
+ }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 790773f..b229212 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "true");
+ DEFAULT_FLAGS.put("settings_silky_home", "false");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 68a6de8..7843411 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,7 @@
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -765,21 +765,21 @@
/**
* Gets an array of support hashing algorithms that can be used to generate the hash of the
* screenshot. The String value of one algorithm should be used when requesting to generate
- * the impression attestation token.
+ * the ScreenshotHash.
*
* @return a String array of supported hashing algorithms.
*/
- String[] getSupportedImpressionAlgorithms();
+ String[] getSupportedScreenshotHashingAlgorithms();
/**
- * Validate the impression token was generated by the system. The impression token passed in
- * should be the token generated when calling {@link IWindowSession#generateImpressionToken}
+ * Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in
+ * should be the token generated when calling {@link IWindowSession#generateScreenshotHash}
*
- * @param impressionToken The token to verify that it was generated by the system.
+ * @param ScreenshotHash The token to verify that it was generated by the system.
* @return true if the token was generated by the system or false if the token cannot be
* verified.
*/
- boolean verifyImpressionToken(in ImpressionToken impressionToken);
+ boolean verifyScreenshotHash(in ScreenshotHash screenshotHash);
/**
* Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 85498cb..7b15f52 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,7 +22,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.InputChannel;
@@ -345,14 +345,14 @@
void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
/**
- * Generates an impression token that can be used to validate whether specific content was on
+ * Generates an ScreenshotHash that can be used to validate whether specific content was on
* screen.
*
- * @param window The token for the window where the view to attest is shown.
+ * @param window The token for the window where the view to screenshot is shown.
* @param boundsInWindow The size and position of the ads view in the window
* @param hashAlgorithm The String for the hashing algorithm to use based on values returned
- * from {@link IWindowManager#getSupportedImpressionAlgorithms()}
+ * from {@link IWindowManager#getSupportedHashingAlgorithms()}
*/
- ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow,
+ ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm);
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index dd56c15..5ae66e3 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,7 +24,7 @@
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
@@ -466,7 +466,7 @@
}
@Override
- public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+ public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
return null;
}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 98738ef..93b2d8a 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -30,14 +30,19 @@
import android.graphics.drawable.Icon;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.RemotableViewMethod;
import android.view.View;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import java.time.Clock;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.util.Formatter;
+import java.util.Locale;
/**
* This widget display an analogic clock with two hands for hours and
@@ -47,15 +52,19 @@
* @attr ref android.R.styleable#AnalogClock_hand_hour
* @attr ref android.R.styleable#AnalogClock_hand_minute
* @attr ref android.R.styleable#AnalogClock_hand_second
+ * @attr ref android.R.styleable#AnalogClock_timeZone
* @deprecated This widget is no longer supported.
*/
@RemoteView
@Deprecated
public class AnalogClock extends View {
+ private static final String LOG_TAG = "AnalogClock";
/** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
private Clock mClock;
+ @Nullable
+ private ZoneId mTimeZone;
@UnsupportedAppUsage
private Drawable mHourHand;
@@ -114,7 +123,8 @@
mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second);
- mClock = Clock.systemDefaultZone();
+ mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
+ createClock();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
@@ -162,6 +172,46 @@
invalidate();
}
+ /**
+ * Indicates which time zone is currently used by this view.
+ *
+ * @return The ID of the current time zone or null if the default time zone,
+ * as set by the user, must be used
+ *
+ * @see java.util.TimeZone
+ * @see java.util.TimeZone#getAvailableIDs()
+ * @see #setTimeZone(String)
+ */
+ @InspectableProperty
+ @Nullable
+ public String getTimeZone() {
+ ZoneId zoneId = mTimeZone;
+ return zoneId == null ? null : zoneId.getId();
+ }
+
+ /**
+ * Sets the specified time zone to use in this clock. When the time zone
+ * is set through this method, system time zone changes (when the user
+ * sets the time zone in settings for instance) will be ignored.
+ *
+ * @param timeZone The desired time zone's ID as specified in {@link java.util.TimeZone}
+ * or null to user the time zone specified by the user
+ * (system time zone)
+ *
+ * @see #getTimeZone()
+ * @see java.util.TimeZone#getAvailableIDs()
+ * @see java.util.TimeZone#getTimeZone(String)
+ *
+ * @attr ref android.R.styleable#AnalogClock_timeZone
+ */
+ @RemotableViewMethod
+ public void setTimeZone(@Nullable String timeZone) {
+ mTimeZone = toZoneId(timeZone);
+
+ createClock();
+ onTimeChanged();
+ }
+
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
@@ -198,8 +248,8 @@
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
// in the main thread, therefore the receiver can't run before this method returns.
- // The time zone may have changed while the receiver wasn't registered, so update the Time
- mClock = Clock.systemDefaultZone();
+ // The time zone may have changed while the receiver wasn't registered, so update the clock.
+ createClock();
// Make sure we update to the current time
onTimeChanged();
@@ -340,8 +390,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
- mClock = Clock.system(ZoneId.of(tz));
+ createClock();
}
onTimeChanged();
@@ -365,9 +414,26 @@
}
};
+ private void createClock() {
+ ZoneId zoneId = mTimeZone;
+ if (zoneId == null) {
+ mClock = Clock.systemDefaultZone();
+ } else {
+ mClock = Clock.system(zoneId);
+ }
+ }
+
private void updateContentDescription(long timeMillis) {
final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;
- String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags);
+ String contentDescription =
+ DateUtils.formatDateRange(
+ mContext,
+ new Formatter(new StringBuilder(50), Locale.getDefault()),
+ timeMillis /* startMillis */,
+ timeMillis /* endMillis */,
+ flags,
+ getTimeZone())
+ .toString();
setContentDescription(contentDescription);
}
@@ -378,4 +444,22 @@
Instant instant = Instant.ofEpochMilli(timeMillis);
return LocalDateTime.ofInstant(instant, zoneId);
}
+
+ /**
+ * Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there
+ * is an error parsing.
+ */
+ @Nullable
+ private static ZoneId toZoneId(@Nullable String timeZone) {
+ if (timeZone == null) {
+ return null;
+ }
+
+ try {
+ return ZoneId.of(timeZone);
+ } catch (DateTimeException e) {
+ Log.w(LOG_TAG, "Failed to parse time zone from " + timeZone, e);
+ return null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e0f9554..3d896c8 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -59,6 +59,7 @@
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
+import com.android.internal.util.PerfettoTrigger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java
deleted file mode 100644
index 643d24a..0000000
--- a/core/java/com/android/internal/jank/PerfettoTrigger.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- */
-
-//TODO (165884885): Make PerfettoTrigger more generic and move it to another package.
-package com.android.internal.jank;
-
-import android.annotation.NonNull;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-/**
- * A trigger implementation with perfetto backend.
- * @hide
- */
-public class PerfettoTrigger {
- private static final String TAG = PerfettoTrigger.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
-
- /**
- * @param triggerName The name of the trigger. Must match the value defined in the AOT
- * Perfetto config.
- */
- public static void trigger(String triggerName) {
- try {
- ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
- if (DEBUG) {
- StringBuilder sb = new StringBuilder();
- for (String arg : pb.command()) {
- sb.append(arg).append(" ");
- }
- Log.d(TAG, "Triggering " + sb.toString());
- }
- Process process = pb.start();
- if (DEBUG) {
- readConsoleOutput(process);
- }
- } catch (IOException | InterruptedException e) {
- Log.w(TAG, "Failed to trigger " + triggerName, e);
- }
- }
-
- private static void readConsoleOutput(@NonNull Process process)
- throws IOException, InterruptedException {
- process.waitFor();
- try (BufferedReader errReader =
- new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
- StringBuilder errLine = new StringBuilder();
- String line;
- while ((line = errReader.readLine()) != null) {
- errLine.append(line).append("\n");
- }
- errLine.append(", code=").append(process.exitValue());
- Log.d(TAG, "err message=" + errLine.toString());
- }
- }
-}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index ea2fb88..964568c 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -114,14 +114,9 @@
.setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
.setConsumedPower(batteryStatsHelper.getTotalPower());
- final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
- for (int i = 0; i < usageList.size(); i++) {
- final BatterySipper sipper = usageList.get(i);
- if (sipper.drainType == BatterySipper.DrainType.APP) {
- batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(sipper.uidObj)
- .setPackageWithHighestDrain(sipper.packageWithHighestDrain)
- .setConsumedPower(sipper.sumPower());
- }
+ SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
+ for (int i = uidStats.size() - 1; i >= 0; i--) {
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
}
final long realtimeUs = SystemClock.elapsedRealtime() * 1000;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fda87be..4b343af 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -103,7 +103,7 @@
*/
public static final int PROFILE_FROM_SHELL = 1 << 15;
- /*
+ /**
* Enable using the ART app image startup cache
*/
public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
@@ -116,13 +116,6 @@
*/
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
- /**
- * Disable runtime access to {@link android.annotation.TestApi} annotated members.
- *
- * <p>This only takes effect if Hidden API access restrictions are enabled as well.
- */
- public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
-
public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
/**
* Enable pointer tagging in this process.
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 30cd94c..f42f468 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,6 +14,7 @@
package com.android.internal.util;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
@@ -23,9 +24,12 @@
import android.util.Log;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.EventLogTags;
import com.android.internal.os.BackgroundThread;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -91,6 +95,34 @@
*/
public static final int ACTION_START_RECENTS_ANIMATION = 8;
+ private static final int[] ACTIONS_ALL = {
+ ACTION_EXPAND_PANEL,
+ ACTION_TOGGLE_RECENTS,
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ ACTION_CHECK_CREDENTIAL,
+ ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ ACTION_TURN_ON_SCREEN,
+ ACTION_ROTATE_SCREEN,
+ ACTION_FACE_WAKE_AND_UNLOCK,
+ ACTION_START_RECENTS_ANIMATION
+ };
+
+ /** @hide */
+ @IntDef({
+ ACTION_EXPAND_PANEL,
+ ACTION_TOGGLE_RECENTS,
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ ACTION_CHECK_CREDENTIAL,
+ ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ ACTION_TURN_ON_SCREEN,
+ ACTION_ROTATE_SCREEN,
+ ACTION_FACE_WAKE_AND_UNLOCK,
+ ACTION_START_RECENTS_ANIMATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {
+ }
+
private static final int[] STATSD_ACTION = new int[]{
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
@@ -105,9 +137,14 @@
private static LatencyTracker sLatencyTracker;
+ private final Object mLock = new Object();
private final SparseLongArray mStartRtc = new SparseLongArray();
- private volatile int mSamplingInterval;
- private volatile boolean mEnabled;
+ @GuardedBy("mLock")
+ private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
+ @GuardedBy("mLock")
+ private int mSamplingInterval;
+ @GuardedBy("mLock")
+ private boolean mEnabled;
public static LatencyTracker getInstance(Context context) {
if (sLatencyTracker == null) {
@@ -132,20 +169,26 @@
}
private void updateProperties(DeviceConfig.Properties properties) {
- mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
- DEFAULT_SAMPLING_INTERVAL);
- mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ synchronized (mLock) {
+ mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ for (int action : ACTIONS_ALL) {
+ mTraceThresholdPerAction[action] =
+ properties.getInt(getTraceTriggerNameForAction(action), -1);
+ }
+ }
}
/**
* A helper method to translate action type to name.
*
- * @param action the action type defined in AtomsProto.java
+ * @param atomsProtoAction the action type defined in AtomsProto.java
* @return the name of the action
*/
- public static String getNameOfAction(int action) {
+ public static String getNameOfAction(int atomsProtoAction) {
// Defined in AtomsProto.java
- switch (action) {
+ switch (atomsProtoAction) {
case 0:
return "UNKNOWN";
case 1:
@@ -171,16 +214,22 @@
}
}
- private static String getTraceNameOfAction(int action) {
+ private static String getTraceNameOfAction(@Action int action) {
return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
}
+ private static String getTraceTriggerNameForAction(@Action int action) {
+ return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
+ }
+
public static boolean isEnabled(Context ctx) {
return getInstance(ctx).isEnabled();
}
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mLock) {
+ return mEnabled;
+ }
}
/**
@@ -188,7 +237,7 @@
*
* @param action The action to start. One of the ACTION_* values.
*/
- public void onActionStart(int action) {
+ public void onActionStart(@Action int action) {
if (!isEnabled()) {
return;
}
@@ -201,7 +250,7 @@
*
* @param action The action to end. One of the ACTION_* values.
*/
- public void onActionEnd(int action) {
+ public void onActionEnd(@Action int action) {
if (!isEnabled()) {
return;
}
@@ -221,19 +270,30 @@
* @param action The action to end. One of the ACTION_* values.
* @param duration The duration of the action in ms.
*/
- public void logAction(int action, int duration) {
- boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ public void logAction(@Action int action, int duration) {
+ boolean shouldSample;
+ int traceThreshold;
+ synchronized (mLock) {
+ shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ traceThreshold = mTraceThresholdPerAction[action];
+ }
+
+ if (traceThreshold > 0 && duration >= traceThreshold) {
+ PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
+ }
+
logActionDeprecated(action, duration, shouldSample);
}
/**
* Logs an action that has started and ended. This needs to be called from the main thread.
*
- * @param action The action to end. One of the ACTION_* values.
- * @param duration The duration of the action in ms.
+ * @param action The action to end. One of the ACTION_* values.
+ * @param duration The duration of the action in ms.
* @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
*/
- public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
+ public static void logActionDeprecated(
+ @Action int action, int duration, boolean writeToStatsLog) {
Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
new file mode 100644
index 0000000..9c87c69
--- /dev/null
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A trigger implementation with perfetto backend.
+ * @hide
+ */
+public class PerfettoTrigger {
+ private static final String TAG = "PerfettoTrigger";
+ private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+
+ /**
+ * @param triggerName The name of the trigger. Must match the value defined in the AOT
+ * Perfetto config.
+ */
+ public static void trigger(String triggerName) {
+ try {
+ ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
+ Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
+ Process process = pb.start();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to trigger " + triggerName, e);
+ }
+ }
+}
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 66753e4..f1998a5 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -47,6 +47,7 @@
static struct assetsprovider_offsets_t {
jclass classObject;
jmethodID loadAssetFd;
+ jmethodID toString;
} gAssetsProviderOffsets;
static struct {
@@ -72,9 +73,22 @@
class LoaderAssetsProvider : public AssetsProvider {
public:
static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
- return (!assets_provider) ? nullptr
+ return (!assets_provider) ? EmptyAssetsProvider::Create()
: std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
- env->NewGlobalRef(assets_provider)));
+ env, assets_provider));
+ }
+
+ bool ForEachFile(const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+ }
+
+ const std::string& GetDebugName() const override {
+ return debug_name_;
+ }
+
+ bool IsUpToDate() const override {
+ return true;
}
~LoaderAssetsProvider() override {
@@ -142,20 +156,26 @@
*file_exists = true;
}
- return ApkAssets::CreateAssetFromFd(base::unique_fd(fd),
- nullptr /* path */,
- static_cast<off64_t>(mOffset),
- static_cast<off64_t>(mLength));
+ return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd),
+ nullptr /* path */,
+ static_cast<off64_t>(mOffset),
+ static_cast<off64_t>(mLength));
}
private:
DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider);
- explicit LoaderAssetsProvider(jobject assets_provider)
- : assets_provider_(assets_provider) { }
+ explicit LoaderAssetsProvider(JNIEnv* env, jobject assets_provider) {
+ assets_provider_ = env->NewGlobalRef(assets_provider);
+ auto string_result = static_cast<jstring>(env->CallObjectMethod(
+ assets_provider_, gAssetsProviderOffsets.toString));
+ ScopedUtfChars str(env, string_result);
+ debug_name_ = std::string(str.c_str(), str.size());
+ }
// The global reference to the AssetsProvider
jobject assets_provider_;
+ std::string debug_name_;
};
static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -170,18 +190,26 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::Load(path.c_str(), property_flags, std::move(loader_assets));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ ZipAssetsProvider::Create(path.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_IDMAP:
apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
break;
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTable(path.c_str(), property_flags, std::move(loader_assets));
+ apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
+ std::move(loader_assets),
+ property_flags);
break;
- case FORMAT_DIRECTORY:
- apk_assets = ApkAssets::LoadFromDir(path.c_str(), property_flags, std::move(loader_assets));
+ case FORMAT_DIRECTORY: {
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ DirectoryAssetsProvider::Create(path.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -221,13 +249,17 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(
+ std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets));
+ apk_assets = ApkAssets::LoadTable(
+ AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
+ std::move(loader_assets), property_flags);
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -282,17 +314,20 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets),
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(
+ std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(),
+ static_cast<off64_t>(offset), static_cast<off64_t>(length)));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets),
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length));
+ apk_assets = ApkAssets::LoadTable(
+ AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(length)),
+ std::move(loader_assets), property_flags);
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -310,8 +345,7 @@
}
static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
- auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
- auto apk_assets = ApkAssets::LoadEmpty(flags, std::move(loader_assets));
+ auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
return reinterpret_cast<jlong>(apk_assets.release());
}
@@ -458,6 +492,8 @@
gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
env, gAssetsProviderOffsets.classObject, "loadAssetFd",
"(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
+ gAssetsProviderOffsets.toString = GetMethodIDOrDie(
+ env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;");
jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5f2dcdf..5630a1e 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -41,6 +41,10 @@
#define ENCODING_OPUS 20
#define ENCODING_PCM_24BIT_PACKED 21
#define ENCODING_PCM_32BIT 22
+#define ENCODING_MPEGH_BL_L3 23
+#define ENCODING_MPEGH_BL_L4 24
+#define ENCODING_MPEGH_LC_L3 25
+#define ENCODING_MPEGH_LC_L4 26
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -98,6 +102,14 @@
return AUDIO_FORMAT_PCM_24_BIT_PACKED;
case ENCODING_PCM_32BIT:
return AUDIO_FORMAT_PCM_32_BIT;
+ case ENCODING_MPEGH_BL_L3:
+ return AUDIO_FORMAT_MPEGH_BL_L3;
+ case ENCODING_MPEGH_BL_L4:
+ return AUDIO_FORMAT_MPEGH_BL_L4;
+ case ENCODING_MPEGH_LC_L3:
+ return AUDIO_FORMAT_MPEGH_LC_L3;
+ case ENCODING_MPEGH_LC_L4:
+ return AUDIO_FORMAT_MPEGH_LC_L4;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -159,6 +171,14 @@
return ENCODING_DOLBY_MAT;
case AUDIO_FORMAT_OPUS:
return ENCODING_OPUS;
+ case AUDIO_FORMAT_MPEGH_BL_L3:
+ return ENCODING_MPEGH_BL_L3;
+ case AUDIO_FORMAT_MPEGH_BL_L4:
+ return ENCODING_MPEGH_BL_L4;
+ case AUDIO_FORMAT_MPEGH_LC_L3:
+ return ENCODING_MPEGH_LC_L3;
+ case AUDIO_FORMAT_MPEGH_LC_L4:
+ return ENCODING_MPEGH_LC_L4;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 989e25d..5442c03 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5412,11 +5412,12 @@
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.service.attestation.ImpressionAttestationService}
- to ensure that only the system can bind to it.
- @hide This is not a third-party API (intended for OEMs and system apps).
+ <!-- Must be required by a
+ {@link android.service.screenshot.ScreenshotHasherService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
-->
- <permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE"
+ <permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE"
android:protectionLevel="signature" />
<!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 39e60d0..a8ef2cf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4062,6 +4062,12 @@
<attr name="hand_hour" format="reference"/>
<attr name="hand_minute" format="reference"/>
<attr name="hand_second" format="reference"/>
+ <!-- Specifies the time zone to use. When this attribute is specified, the
+ TextClock will ignore the time zone of the system. To use the user's
+ time zone, do not specify this attribute. The default value is the
+ user's time zone. Please refer to {@link java.util.TimeZone} for more
+ information about time zone ids. -->
+ <attr name="timeZone" format="string"/>
</declare-styleable>
<declare-styleable name="Button">
</declare-styleable>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 96e9c4a..7b1ca8f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -73,7 +73,6 @@
final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo");
- uidBatteryConsumerBuilder.setConsumedPower(200);
uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300);
uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
@@ -90,7 +89,6 @@
final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
builder.getOrCreateSystemBatteryConsumerBuilder(
SystemBatteryConsumer.DRAIN_TYPE_CAMERA);
- systemBatteryConsumerBuilder.setConsumedPower(10000);
systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
@@ -114,7 +112,6 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == 2000) {
assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(200);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300);
assertThat(uidBatteryConsumer.getConsumedPower(
@@ -130,6 +127,7 @@
BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
@@ -139,7 +137,6 @@
batteryUsageStats.getSystemBatteryConsumers();
for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) {
if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) {
- assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(10000);
assertThat(systemBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
@@ -151,6 +148,7 @@
BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
+ assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510);
} else {
fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 52648d9..fe97e24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -16,7 +16,7 @@
package com.android.wm.shell;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.ShellExecutor;
@@ -155,7 +155,7 @@
}
final int taskId = new Integer(args[2]);
final int sideStagePosition = args.length > 3
- ? new Integer(args[3]) : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 800150c..35dcdd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -34,8 +34,11 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -84,8 +87,7 @@
private DragSession mSession;
public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
- this(context, ActivityTaskManager.getInstance(), splitScreen,
- new DefaultStarter(context, splitScreen));
+ this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
}
@VisibleForTesting
@@ -94,7 +96,7 @@
mContext = context;
mActivityTaskManager = activityTaskManager;
mSplitScreen = splitScreen;
- mStarter = starter;
+ mStarter = mSplitScreen != null ? mSplitScreen : starter;
}
/**
@@ -195,39 +197,23 @@
return;
}
- final ClipDescription description = data.getDescription();
- final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
- final Intent dragData = mSession.dragData;
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- final Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
- ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
- : new Bundle();
- if (target.type == TYPE_FULLSCREEN) {
- // Exit split stages if needed
- mStarter.exitSplitScreen();
- } else if (mSplitScreen != null) {
+ @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
+ @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+ if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
- final int position = leftOrTop
- ? SIDE_STAGE_POSITION_TOP_OR_LEFT : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
if (!inSplitScreen) {
- // Update the side stage position to match where we want to launch.
- mSplitScreen.setSideStagePosition(position);
+ // Launch in the side stage if we are not in split-screen already.
+ stage = STAGE_TYPE_SIDE;
}
- mSplitScreen.updateActivityOptions(opts, position);
}
- if (isTask) {
- mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
- } else if (isShortcut) {
- mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
- dragData.getStringExtra(EXTRA_SHORTCUT_ID),
- opts, dragData.getParcelableExtra(EXTRA_USER));
- } else {
- mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
- }
+ final ClipDescription description = data.getDescription();
+ final Intent dragData = mSession.dragData;
+ mStarter.startClipDescription(description, dragData, stage, position);
}
/**
@@ -247,7 +233,6 @@
int runningTaskActType = ACTIVITY_TYPE_STANDARD;
boolean runningTaskIsResizeable;
boolean dragItemSupportsSplitscreen;
- boolean isPhone;
DragSession(Context context, ActivityTaskManager activityTaskManager,
DisplayLayout dispLayout, ClipData data) {
@@ -275,7 +260,6 @@
final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
dragItemSupportsSplitscreen = info == null
|| ActivityInfo.isResizeableMode(info.resizeMode);
- isPhone = mContext.getResources().getConfiguration().smallestScreenWidthDp < 600;
dragData = mInitialDragData.getItemAt(0).getIntent();
}
}
@@ -284,11 +268,33 @@
* Interface for actually committing the task launches.
*/
@VisibleForTesting
- interface Starter {
- void startTask(int taskId, Bundle activityOptions);
- void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
- UserHandle user);
- void startIntent(PendingIntent intent, Bundle activityOptions);
+ public interface Starter {
+ default void startClipDescription(ClipDescription description, Intent intent,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+ if (isTask) {
+ final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ startTask(taskId, stage, position, opts);
+ } else if (isShortcut) {
+ final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+ startShortcut(packageName, id, stage, position, opts, user);
+ } else {
+ startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
+ }
+ }
+ void startTask(int taskId, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
}
@@ -299,39 +305,39 @@
*/
private static class DefaultStarter implements Starter {
private final Context mContext;
- private final SplitScreen mSplitScreen;
- public DefaultStarter(Context context, SplitScreen splitScreen) {
+ public DefaultStarter(Context context) {
mContext = context;
- mSplitScreen = splitScreen;
}
@Override
- public void startTask(int taskId, Bundle activityOptions) {
+ public void startTask(int taskId, int stage, int position,
+ @Nullable Bundle options) {
try {
- ActivityTaskManager.getService().startActivityFromRecents(taskId, activityOptions);
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to launch task", e);
}
}
@Override
- public void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
- UserHandle user) {
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ @Nullable Bundle options, UserHandle user) {
try {
LauncherApps launcherApps =
mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
- activityOptions, user);
+ options, user);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Failed to launch shortcut", e);
}
}
@Override
- public void startIntent(PendingIntent intent, Bundle activityOptions) {
+ public void startIntent(PendingIntent intent, int stage, int position,
+ @Nullable Bundle options) {
try {
- intent.send(null, 0, null, null, null, null, activityOptions);
+ intent.send(null, 0, null, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
@@ -339,14 +345,12 @@
@Override
public void enterSplitScreen(int taskId, boolean leftOrTop) {
- mSplitScreen.moveToSideStage(taskId,
- leftOrTop ? SIDE_STAGE_POSITION_TOP_OR_LEFT
- : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+ throw new UnsupportedOperationException("enterSplitScreen not implemented by starter");
}
@Override
public void exitSplitScreen() {
- mSplitScreen.exitSplitScreen();
+ throw new UnsupportedOperationException("exitSplitScreen not implemented by starter");
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 2df3e2e..962c467 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -231,7 +231,6 @@
&& (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
mAllowTouches = !disallowTouchesUntilAnimationEnd;
cancelDelayedHide();
- updateActionViews(stackBounds);
if (mMenuContainerAnimator != null) {
mMenuContainerAnimator.cancel();
}
@@ -280,6 +279,7 @@
setVisibility(VISIBLE);
mMenuContainerAnimator.start();
}
+ updateActionViews(stackBounds);
} else {
// If we are already visible, then just start the delayed dismiss and unregister any
// existing input consumers from the previous drag
@@ -395,7 +395,7 @@
return true;
});
- if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+ if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
} else {
actionsContainer.setVisibility(View.VISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 7c1b9d8..2c68092 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,12 +18,16 @@
import android.annotation.IntDef;
import android.app.ActivityManager;
+import android.app.PendingIntent;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import java.io.PrintWriter;
@@ -31,46 +35,93 @@
* Interface to engage split-screen feature.
*/
@ExternalThread
-public interface SplitScreen {
+public interface SplitScreen extends DragAndDropPolicy.Starter {
/**
- * Specifies that the side-stage is positioned at the top half of the screen if
+ * Stage position isn't specified normally meaning to use what ever it is currently set to.
+ */
+ int STAGE_POSITION_UNDEFINED = -1;
+ /**
+ * Specifies that a stage is positioned at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
*/
- int SIDE_STAGE_POSITION_TOP_OR_LEFT = 0;
+ int STAGE_POSITION_TOP_OR_LEFT = 0;
/**
- * Specifies that the side-stage is positioned at the bottom half of the screen if
+ * Specifies that a stage is positioned at the bottom half of the screen if
* in portrait mode or at the right half of the screen if in landscape mode.
*/
- int SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+ int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
- @IntDef(prefix = { "SIDE_STAGE_POSITION_" }, value = {
- SIDE_STAGE_POSITION_TOP_OR_LEFT,
- SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT
+ @IntDef(prefix = { "STAGE_POSITION_" }, value = {
+ STAGE_POSITION_UNDEFINED,
+ STAGE_POSITION_TOP_OR_LEFT,
+ STAGE_POSITION_BOTTOM_OR_RIGHT
})
- @interface SideStagePosition {}
+ @interface StagePosition {}
+
+ /**
+ * Stage type isn't specified normally meaning to use what ever the default is.
+ * E.g. exit split-screen and launch the app in fullscreen.
+ */
+ int STAGE_TYPE_UNDEFINED = -1;
+ /**
+ * The main stage type.
+ * @see MainStage
+ */
+ int STAGE_TYPE_MAIN = 0;
+
+ /**
+ * The side stage type.
+ * @see SideStage
+ */
+ int STAGE_TYPE_SIDE = 1;
+
+ @IntDef(prefix = { "STAGE_TYPE_" }, value = {
+ STAGE_TYPE_UNDEFINED,
+ STAGE_TYPE_MAIN,
+ STAGE_TYPE_SIDE
+ })
+ @interface StageType {}
+
+ /** Callback interface for listening to changes in a split-screen stage. */
+ interface SplitScreenListener {
+ void onStagePositionChanged(@StageType int stage, @StagePosition int position);
+ void onTaskStageChanged(int taskId, @StageType int stage);
+ }
/** @return {@code true} if split-screen is currently visible. */
boolean isSplitScreenVisible();
/** Moves a task in the side-stage of split-screen. */
- boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition);
+ boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition);
/** Moves a task in the side-stage of split-screen. */
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SideStagePosition int sideStagePosition);
+ @StagePosition int sideStagePosition);
/** Removes a task from the side-stage of split-screen. */
boolean removeFromSideStage(int taskId);
/** Sets the position of the side-stage. */
- void setSideStagePosition(@SideStagePosition int sideStagePosition);
+ void setSideStagePosition(@StagePosition int sideStagePosition);
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(boolean visible);
+ default void enterSplitScreen(int taskId, boolean leftOrTop) {
+ moveToSideStage(taskId,
+ leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+ }
/** Removes the split-screen stages. */
void exitSplitScreen();
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
- /** Updates the launch activity options for the split position we want to launch it in. */
- void updateActivityOptions(Bundle opts, @SideStagePosition int position);
/** Dumps current status of split-screen. */
void dump(@NonNull PrintWriter pw, String prefix);
/** Called when the shell organizer has been registered. */
void onOrganizerRegistered();
+
+ void registerSplitScreenListener(SplitScreenListener listener);
+ void unregisterSplitScreenListener(SplitScreenListener listener);
+
+ void startTask(int taskId,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 27d3b81..18dd53b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -19,11 +19,19 @@
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -69,7 +77,7 @@
}
@Override
- public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
+ public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -79,7 +87,7 @@
@Override
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SideStagePosition int sideStagePosition) {
+ @StagePosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
@@ -89,7 +97,7 @@
}
@Override
- public void setSideStagePosition(@SideStagePosition int sideStagePosition) {
+ public void setSideStagePosition(@StagePosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
@@ -109,8 +117,103 @@
}
@Override
- public void updateActivityOptions(Bundle opts, @SideStagePosition int position) {
- mStageCoordinator.updateActivityOptions(opts, position);
+ public void registerSplitScreenListener(SplitScreenListener listener) {
+ mStageCoordinator.registerSplitScreenListener(listener);
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ mStageCoordinator.unregisterSplitScreenListener(listener);
+ }
+
+ @Override
+ public void startTask(int taskId,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ LauncherApps launcherApps =
+ mContext.getSystemService(LauncherApps.class);
+ launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
+ options, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Failed to launch shortcut", e);
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ intent.send(null, 0, null, null, null, null, options);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch activity", e);
+ }
+ }
+
+ private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
+ @Nullable Bundle options) {
+ switch (stage) {
+ case STAGE_TYPE_UNDEFINED: {
+ // Use the stage of the specified position is valid.
+ if (position != STAGE_POSITION_UNDEFINED) {
+ if (position == mStageCoordinator.getSideStagePosition()) {
+ options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
+ } else {
+ options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
+ }
+ } else {
+ // Exit split-screen and launch fullscreen since stage wasn't specified.
+ mStageCoordinator.exitSplitScreen();
+ }
+ break;
+ }
+ case STAGE_TYPE_SIDE: {
+ if (position != STAGE_POSITION_UNDEFINED) {
+ mStageCoordinator.setSideStagePosition(position);
+ } else {
+ position = mStageCoordinator.getSideStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ mStageCoordinator.updateActivityOptions(options, position);
+ break;
+ }
+ case STAGE_TYPE_MAIN: {
+ if (position != STAGE_POSITION_UNDEFINED) {
+ // Set the side stage opposite of what we want to the main stage.
+ final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ mStageCoordinator.setSideStagePosition(sideStagePosition);
+ } else {
+ position = mStageCoordinator.getMainStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ mStageCoordinator.updateActivityOptions(options, position);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown stage=" + stage);
+ }
+
+ return options;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d571e75..176852b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -17,12 +17,14 @@
package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.content.Context;
@@ -41,6 +43,8 @@
import com.android.wm.shell.common.split.SplitLayout;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -64,8 +68,7 @@
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private @SplitScreen.SideStagePosition int mSideStagePosition =
- SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT;
private final int mDisplayId;
private SplitLayout mSplitLayout;
@@ -75,6 +78,7 @@
private final ShellTaskOrganizer mTaskOrganizer;
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
+ private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -107,7 +111,7 @@
}
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitScreen.SideStagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStagePosition = sideStagePosition;
mMainStage.activate(getMainStageBounds(), wct);
@@ -130,7 +134,16 @@
return result;
}
- void setSideStagePosition(@SplitScreen.SideStagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int getSideStagePosition() {
+ return mSideStagePosition;
+ }
+
+ @SplitScreen.StagePosition int getMainStagePosition() {
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ }
+
+ void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
@@ -163,7 +176,7 @@
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
}
- void updateActivityOptions(Bundle opts, @SplitScreen.SideStagePosition int position) {
+ void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) {
final StageTaskListener stage = position == mSideStagePosition ? mSideStage : mMainStage;
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
@@ -176,6 +189,35 @@
}
}
+ void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+ if (mListeners.contains(listener)) return;
+ mListeners.add(listener);
+ listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
+ mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ }
+
+ void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void onStageChildTaskStatusChanged(
+ StageListenerImpl stageListener, int taskId, boolean present) {
+
+ int stage;
+ if (present) {
+ stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ } else {
+ // No longer on any stage
+ stage = STAGE_TYPE_UNDEFINED;
+ }
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onTaskStageChanged(taskId, stage);
+ }
+ }
+
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -299,7 +341,7 @@
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop = bottomOrRight
- && mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
}
@@ -326,8 +368,8 @@
@Override
public void onDoubleTappedDivider() {
- setSideStagePosition(mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
- ? SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT : SIDE_STAGE_POSITION_TOP_OR_LEFT);
+ setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
}
@Override
@@ -380,12 +422,12 @@
}
private Rect getSideStageBounds() {
- return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
}
private Rect getMainStageBounds() {
- return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
}
@@ -429,6 +471,11 @@
}
@Override
+ public void onChildTaskStatusChanged(int taskId, boolean present) {
+ StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present);
+ }
+
+ @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1aa7552..6532993 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -58,6 +58,7 @@
public interface StageListenerCallbacks {
void onRootTaskAppeared();
void onStatusChanged(boolean visible, boolean hasChildren);
+ void onChildTaskStatusChanged(int taskId, boolean present);
void onRootTaskVanished();
}
private final StageListenerCallbacks mCallbacks;
@@ -83,9 +84,11 @@
mRootTaskInfo = taskInfo;
mCallbacks.onRootTaskAppeared();
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
- mChildrenLeashes.put(taskInfo.taskId, leash);
- mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
+ final int taskId = taskInfo.taskId;
+ mChildrenLeashes.put(taskId, leash);
+ mChildrenTaskInfo.put(taskId, taskInfo);
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
+ mCallbacks.onChildTaskStatusChanged(taskId, true /* present */);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -120,6 +123,7 @@
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
sendStatusChanged();
+ mCallbacks.onChildTaskStatusChanged(taskId, false /* present */);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -134,6 +138,13 @@
wct.reorder(mRootTaskInfo.token, visible /* onTop */);
}
+ void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
+ @SplitScreen.StageType int stage) {
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ listener.onTaskStageChanged(mChildrenTaskInfo.keyAt(i), stage);
+ }
+ }
+
private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash, boolean firstAppeared) {
final Point taskPositionInParent = taskInfo.positionInParent;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 79bdaf4..2572106 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -29,6 +29,10 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -86,11 +90,9 @@
@Mock
private ActivityTaskManager mActivityTaskManager;
+ // Both the split-screen and start interface.
@Mock
- private SplitScreen mSplitScreen;
-
- @Mock
- private DragAndDropPolicy.Starter mStarter;
+ private SplitScreen mSplitScreenStarter;
private DisplayLayout mLandscapeDisplayLayout;
private DisplayLayout mPortraitDisplayLayout;
@@ -126,7 +128,7 @@
mInsets = Insets.of(0, 0, 0, 0);
mPolicy = new DragAndDropPolicy(
- mContext, mActivityTaskManager, mSplitScreen, mStarter);
+ mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -191,7 +193,7 @@
}
private void setInSplitScreen(boolean inSplitscreen) {
- doReturn(inSplitscreen).when(mSplitScreen).isSplitScreenVisible();
+ doReturn(inSplitscreen).when(mSplitScreenStarter).isSplitScreenVisible();
}
@Test
@@ -202,7 +204,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -213,12 +216,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).exitSplitScreen();
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -229,12 +233,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).exitSplitScreen();
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -245,7 +250,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -256,7 +262,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -268,12 +275,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -285,12 +294,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 168e0df..d2d1812 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -19,7 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -71,7 +71,7 @@
public void testMoveToSideStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- mStageCoordinator.moveToSideStage(task, SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+ mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT);
verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
verify(mSideStage).addTask(eq(task), any(Rect.class),
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index b54f7d8..4b4284a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -41,6 +41,7 @@
"AssetDir.cpp",
"AssetManager.cpp",
"AssetManager2.cpp",
+ "AssetsProvider.cpp",
"AttributeResolution.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 011a0de..ca5981c0 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -16,533 +16,145 @@
#include "androidfw/ApkAssets.h"
-#include <algorithm>
-
#include "android-base/errors.h"
-#include "android-base/file.h"
#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-#include "utils/Compat.h"
-#include "ziparchive/zip_archive.h"
-
-#include "androidfw/Asset.h"
-#include "androidfw/Idmap.h"
-#include "androidfw/misc.h"
-#include "androidfw/Util.h"
namespace android {
using base::SystemErrorCodeToString;
using base::unique_fd;
-static const std::string kResourcesArsc("resources.arsc");
+constexpr const char* kResourcesArsc = "resources.arsc";
-ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
- std::string path,
- time_t last_mod_time,
- package_property_t property_flags)
- : assets_provider_(std::move(assets_provider)),
- path_(std::move(path)),
- last_mod_time_(last_mod_time),
- property_flags_(property_flags) {
+ApkAssets::ApkAssets(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<LoadedArsc> loaded_arsc,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap)
+ : resources_asset_(std::move(resources_asset)),
+ loaded_arsc_(std::move(loaded_arsc)),
+ assets_provider_(std::move(assets)),
+ property_flags_(property_flags),
+ idmap_asset_(std::move(idmap_asset)),
+ loaded_idmap_(std::move(loaded_idmap)) {}
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path, package_property_t flags) {
+ return Load(ZipAssetsProvider::Create(path), flags);
}
-// Provides asset files from a zip file.
-class ZipAssetsProvider : public AssetsProvider {
- public:
- ~ZipAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
- ::ZipArchiveHandle unmanaged_handle;
- const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
-
- return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
- }
-
- static std::unique_ptr<const AssetsProvider> Create(
- unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
- const off64_t length = ApkAssets::kUnknownLength) {
-
- ::ZipArchiveHandle unmanaged_handle;
- const int32_t result = (length == ApkAssets::kUnknownLength)
- ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
- : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
- offset);
-
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
- << " and length " << length << ": " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
-
- return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
- unmanaged_handle));
- }
-
- // Iterate over all files and directories within the zip. The order of iteration is not
- // guaranteed to be the same as the order of elements in the central directory but is stable for a
- // given zip file.
- bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override {
- // If this is a resource loader from an .arsc, there will be no zip handle
- if (zip_handle_ == nullptr) {
- return false;
- }
-
- std::string root_path_full = root_path;
- if (root_path_full.back() != '/') {
- root_path_full += '/';
- }
-
- void* cookie;
- if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
- return false;
- }
-
- std::string name;
- ::ZipEntry entry{};
-
- // We need to hold back directories because many paths will contain them and we want to only
- // surface one.
- std::set<std::string> dirs{};
-
- int32_t result;
- while ((result = ::Next(cookie, &entry, &name)) == 0) {
- StringPiece full_file_path(name);
- StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-
- if (!leaf_file_path.empty()) {
- auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
- if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
- dirs.insert(std::move(dir));
- } else {
- f(leaf_file_path, kFileTypeRegular);
- }
- }
- }
- ::EndIteration(cookie);
-
- // Now present the unique directories.
- for (const std::string& dir : dirs) {
- f(dir, kFileTypeDirectory);
- }
-
- // -1 is end of iteration, anything else is an error.
- return result == -1;
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
- if (file_exists) {
- *file_exists = false;
- }
-
- ::ZipEntry entry;
- int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
- if (result != 0) {
- return {};
- }
-
- if (file_exists) {
- *file_exists = true;
- }
-
- const int fd = ::GetFileDescriptor(zip_handle_.get());
- const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
- incfs::IncFsFileMap asset_map;
- if (entry.method == kCompressDeflated) {
- if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
- }
-
- if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- unique_fd ufd;
- if (!GetPath()) {
- // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
- // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
- // to create new file descriptors.
- ufd = unique_fd(dup(fd));
- if (!ufd.ok()) {
- LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- }
-
- auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
-
- explicit ZipAssetsProvider(std::string path,
- std::string friendly_name,
- ZipArchiveHandle unmanaged_handle)
- : zip_handle_(unmanaged_handle, ::CloseArchive),
- path_(std::move(path)),
- friendly_name_(std::move(friendly_name)) { }
-
- const char* GetPath() const {
- return path_.empty() ? nullptr : path_.c_str();
- }
-
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
- ZipArchivePtr zip_handle_;
- std::string path_;
- std::string friendly_name_;
-};
-
-class DirectoryAssetsProvider : AssetsProvider {
- public:
- ~DirectoryAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
- struct stat sb{};
- const int result = stat(path.c_str(), &sb);
- if (result == -1) {
- LOG(ERROR) << "Failed to find directory '" << path << "'.";
- return nullptr;
- }
-
- if (!S_ISDIR(sb.st_mode)) {
- LOG(ERROR) << "Path '" << path << "' is not a directory.";
- return nullptr;
- }
-
- return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
- const std::string resolved_path = ResolvePath(path);
- if (file_exists) {
- struct stat sb{};
- const int result = stat(resolved_path.c_str(), &sb);
- *file_exists = result != -1 && S_ISREG(sb.st_mode);
- }
-
- return ApkAssets::CreateAssetFromFile(resolved_path);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
-
- explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
-
- inline std::string ResolvePath(const std::string& path) const {
- return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
- }
-
- const std::string path_;
-};
-
-// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
-class EmptyAssetsProvider : public AssetsProvider {
- public:
- EmptyAssetsProvider() = default;
- ~EmptyAssetsProvider() override = default;
-
- protected:
- std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
- Asset::AccessMode /* mode */,
- bool* file_exists) const override {
- if (file_exists) {
- *file_exists = false;
- }
- return nullptr;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
-};
-
-// AssetProvider implementation
-class MultiAssetsProvider : public AssetsProvider {
- public:
- ~MultiAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(
- std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
- CHECK(parent != nullptr) << "parent provider must not be null";
- return (!child) ? std::move(parent)
- : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
- std::move(child), std::move(parent)));
- }
-
- bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override {
- // TODO: Only call the function once for files defined in the parent and child
- return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
- auto asset = child_->Open(path, mode, file_exists);
- return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
-
- MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
- std::unique_ptr<const AssetsProvider> parent)
- : child_(std::move(child)), parent_(std::move(parent)) { }
-
- std::unique_ptr<const AssetsProvider> child_;
- std::unique_ptr<const AssetsProvider> parent_;
-};
-
-// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
-// file.
-std::unique_ptr<const ApkAssets> ApkAssets::Load(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
- auto assets = ZipAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadFromFd(base::unique_fd fd,
+ const std::string& debug_name,
+ package_property_t flags,
+ off64_t offset,
+ off64_t len) {
+ return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
}
-// Opens the archive using the file file descriptor with the specified file offset and read length.
-// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
- unique_fd fd, const std::string& friendly_name, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
- const off64_t length) {
- CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
- CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
- << kUnknownLength;
-
- auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
- return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::Load(std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags) {
+ return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = CreateAssetFromFile(path);
- return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags) {
+ if (resources_asset == nullptr) {
+ return {};
+ }
+ return LoadImpl(std::move(resources_asset), std::move(assets), flags, nullptr /* idmap_asset */,
+ nullptr /* loaded_idmap */);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
- unique_fd fd, const std::string& friendly_name, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
- const off64_t length) {
-
- auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
- return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
- std::move(override_asset))
- : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
- const package_property_t flags) {
+std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
+ package_property_t flags) {
CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
- std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
+ auto idmap_asset = AssetsProvider::CreateAssetFromFile(idmap_path);
if (idmap_asset == nullptr) {
+ LOG(ERROR) << "failed to read IDMAP " << idmap_path;
return {};
}
- const StringPiece idmap_data(
- reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
- static_cast<size_t>(idmap_asset->getLength()));
- std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
+ StringPiece idmap_data(reinterpret_cast<const char*>(idmap_asset->getBuffer(true /* aligned */)),
+ static_cast<size_t>(idmap_asset->getLength()));
+ auto loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
if (loaded_idmap == nullptr) {
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
-
- auto overlay_path = std::string(loaded_idmap->OverlayApkPath());
- auto assets = ZipAssetsProvider::Create(overlay_path);
- return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
- nullptr /* override_asset */, std::move(idmap_asset),
- std::move(loaded_idmap))
- : nullptr;
-}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = DirectoryAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
- const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = (override_asset) ? std::move(override_asset)
- : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
- -1 /* last_mod-time */, flags));
- loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
-}
-
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
- unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
- if (!fd.ok()) {
- LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
+ const std::string overlay_path(loaded_idmap->OverlayApkPath());
+ auto overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ if (overlay_assets == nullptr) {
return {};
}
- return CreateAssetFromFd(std::move(fd), path.c_str());
+ return LoadImpl(std::move(overlay_assets), flags | PROPERTY_OVERLAY, std::move(idmap_asset),
+ std::move(loaded_idmap));
}
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
- const char* path,
- off64_t offset,
- off64_t length) {
- CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
- CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
- << kUnknownLength;
- if (length == kUnknownLength) {
- length = lseek64(fd, 0, SEEK_END);
- if (length < 0) {
- LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
- << SystemErrorCodeToString(errno);
- return {};
- }
- }
-
- incfs::IncFsFileMap file_map;
- if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
- LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
- << SystemErrorCodeToString(errno);
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ if (assets == nullptr) {
return {};
}
- // If `path` is set, do not pass ownership of the `fd` to the new Asset since
- // Asset::openFileDescriptor can use `path` to create new file descriptors.
- return Asset::createFromUncompressedMap(std::move(file_map),
- Asset::AccessMode::ACCESS_RANDOM,
- (path) ? base::unique_fd(-1) : std::move(fd));
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
- std::unique_ptr<const AssetsProvider> assets, const std::string& path,
- package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
- std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
-
- const time_t last_mod_time = getFileModDate(path.c_str());
-
// Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
bool resources_asset_exists = false;
- auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
- &resources_asset_exists);
-
- assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
-
- // Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets>
- loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
-
- if (!resources_asset_exists) {
- loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
- return std::move(loaded_apk);
- }
-
- loaded_apk->resources_asset_ = std::move(resources_asset_);
- if (!loaded_apk->resources_asset_) {
- LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
+ auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
+ &resources_asset_exists);
+ if (resources_asset == nullptr && resources_asset_exists) {
+ LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << assets->GetDebugName()
+ << "'.";
return {};
}
- // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
- loaded_apk->idmap_asset_ = std::move(idmap_asset);
- loaded_apk->loaded_idmap_ = std::move(idmap);
-
- const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
- const size_t length = loaded_apk->resources_asset_->getLength();
- if (!data || length == 0) {
- LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
- return {};
- }
-
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
- property_flags);
- if (!loaded_apk->loaded_arsc_) {
- LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
- return {};
- }
-
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
+ return LoadImpl(std::move(resources_asset), std::move(assets), property_flags,
+ std::move(idmap_asset), std::move(loaded_idmap));
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
- std::unique_ptr<Asset> resources_asset, const std::string& path,
- package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
-
- const time_t last_mod_time = getFileModDate(path.c_str());
-
- auto assets = (override_assets) ? std::move(override_assets)
- : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
-
- std::unique_ptr<ApkAssets> loaded_apk(
- new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
- loaded_apk->resources_asset_ = std::move(resources_asset);
-
- const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
- const size_t length = loaded_apk->resources_asset_->getLength();
- if (!data || length == 0) {
- LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ if (assets == nullptr ) {
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
- property_flags);
- if (loaded_apk->loaded_arsc_ == nullptr) {
- LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
+ std::unique_ptr<LoadedArsc> loaded_arsc;
+ if (resources_asset != nullptr) {
+ const auto data = resources_asset->getIncFsBuffer(true /* aligned */);
+ const size_t length = resources_asset->getLength();
+ if (!data || length == 0) {
+ LOG(ERROR) << "Failed to read resources table in APK '" << assets->GetDebugName() << "'.";
+ return {};
+ }
+ loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
+ } else {
+ loaded_arsc = LoadedArsc::CreateEmpty();
+ }
+
+ if (loaded_arsc == nullptr) {
+ LOG(ERROR) << "Failed to load resources table in APK '" << assets->GetDebugName() << "'.";
return {};
}
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
+ return std::unique_ptr<ApkAssets>(new ApkAssets(std::move(resources_asset),
+ std::move(loaded_arsc), std::move(assets),
+ property_flags, std::move(idmap_asset),
+ std::move(loaded_idmap)));
+}
+
+const std::string& ApkAssets::GetPath() const {
+ return assets_provider_->GetDebugName();
}
bool ApkAssets::IsUpToDate() const {
- if (IsLoader()) {
- // Loaders are invalidated by the app, not the system, so assume they are up to date.
- return true;
- }
- return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
- last_mod_time_ == getFileModDate(path_.c_str());
+ // Loaders are invalidated by the app, not the system, so assume they are up to date.
+ return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate())
+ && assets_provider_->IsUpToDate());
}
-
} // namespace android
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
new file mode 100644
index 0000000..23cacf8
--- /dev/null
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "androidfw/AssetsProvider.h"
+
+#include <sys/stat.h>
+
+#include <android-base/errors.h>
+#include <android-base/stringprintf.h>
+#include <android-base/utf8.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace {
+constexpr const char* kEmptyDebugString = "<empty>";
+} // namespace
+
+std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const {
+ return OpenInternal(path, mode, file_exists);
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFile(const std::string& path) {
+ base::unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Failed to open file '" << path << "': " << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset,
+ off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+ if (length == kUnknownLength) {
+ length = lseek64(fd, 0, SEEK_END);
+ if (length < 0) {
+ LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+ << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+ }
+
+ incfs::IncFsFileMap file_map;
+ if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
+ LOG(ERROR) << "Failed to mmap file '" << ((path != nullptr) ? path : "anon") << "': "
+ << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ // If `path` is set, do not pass ownership of the `fd` to the new Asset since
+ // Asset::openFileDescriptor can use `path` to create new file descriptors.
+ return Asset::createFromUncompressedMap(std::move(file_map),
+ Asset::AccessMode::ACCESS_RANDOM,
+ (path != nullptr) ? base::unique_fd(-1) : std::move(fd));
+}
+
+ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
+ : value_(std::forward<std::string>(value)), is_path_(is_path) {}
+
+const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
+ return is_path_ ? &value_ : nullptr;
+}
+
+const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
+ return value_;
+}
+
+ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
+ time_t last_mod_time)
+ : zip_handle_(handle, ::CloseArchive),
+ name_(std::forward<PathOrDebugName>(path)),
+ last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
+ ZipArchiveHandle handle;
+ if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+ CloseArchive(handle);
+ return {};
+ }
+
+ struct stat sb{.st_mtime = -1};
+ if (stat(path.c_str(), &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to stat file '" << path << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
+
+ return std::unique_ptr<ZipAssetsProvider>(
+ new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
+ true /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
+ std::string friendly_name,
+ off64_t offset,
+ off64_t len) {
+ ZipArchiveHandle handle;
+ const int released_fd = fd.release();
+ const int32_t result = (len == AssetsProvider::kUnknownLength)
+ ? ::OpenArchiveFd(released_fd, friendly_name.c_str(), &handle)
+ : ::OpenArchiveFdRange(released_fd, friendly_name.c_str(), &handle, len, offset);
+
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
+ << " and length " << len << ": " << ::ErrorCodeString(result);
+ CloseArchive(handle);
+ return {};
+ }
+
+ struct stat sb{.st_mtime = -1};
+ if (fstat(released_fd, &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
+
+ return std::unique_ptr<ZipAssetsProvider>(
+ new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
+ false /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const {
+ if (file_exists != nullptr) {
+ *file_exists = false;
+ }
+
+ ZipEntry entry;
+ if (FindEntry(zip_handle_.get(), path, &entry) != 0) {
+ return {};
+ }
+
+ if (file_exists != nullptr) {
+ *file_exists = true;
+ }
+
+ const int fd = GetFileDescriptor(zip_handle_.get());
+ const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get());
+ incfs::IncFsFileMap asset_map;
+ if (entry.method == kCompressDeflated) {
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length,
+ name_.GetDebugName().c_str())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName()
+ << "'";
+ return {};
+ }
+
+ std::unique_ptr<Asset> asset =
+ Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << name_.GetDebugName()
+ << "'";
+ return {};
+ }
+ return asset;
+ }
+
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length,
+ name_.GetDebugName().c_str())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+
+ base::unique_fd ufd;
+ if (name_.GetPath() == nullptr) {
+ // If the zip name does not represent a path, create a new `fd` for the new Asset to own in
+ // order to create new file descriptors using Asset::openFileDescriptor. If the zip name is a
+ // path, it will be used to create new file descriptors.
+ ufd = base::unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+ }
+
+ auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+ return asset;
+}
+
+bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f)
+ const {
+ std::string root_path_full = root_path;
+ if (root_path_full.back() != '/') {
+ root_path_full += '/';
+ }
+
+ void* cookie;
+ if (StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
+ return false;
+ }
+
+ std::string name;
+ ::ZipEntry entry{};
+
+ // We need to hold back directories because many paths will contain them and we want to only
+ // surface one.
+ std::set<std::string> dirs{};
+
+ int32_t result;
+ while ((result = Next(cookie, &entry, &name)) == 0) {
+ StringPiece full_file_path(name);
+ StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
+
+ if (!leaf_file_path.empty()) {
+ auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+ if (iter != leaf_file_path.end()) {
+ std::string dir =
+ leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ dirs.insert(std::move(dir));
+ } else {
+ f(leaf_file_path, kFileTypeRegular);
+ }
+ }
+ }
+ EndIteration(cookie);
+
+ // Now present the unique directories.
+ for (const std::string& dir : dirs) {
+ f(dir, kFileTypeDirectory);
+ }
+
+ // -1 is end of iteration, anything else is an error.
+ return result == -1;
+}
+
+const std::string& ZipAssetsProvider::GetDebugName() const {
+ return name_.GetDebugName();
+}
+
+bool ZipAssetsProvider::IsUpToDate() const {
+ struct stat sb{};
+ if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
+ // If fstat fails on the zip archive, return true so the zip archive the resource system does
+ // attempt to refresh the ApkAsset.
+ return true;
+ }
+ return last_mod_time_ == sb.st_mtime;
+}
+
+DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
+ : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
+ struct stat sb{};
+ const int result = stat(path.c_str(), &sb);
+ if (result == -1) {
+ LOG(ERROR) << "Failed to find directory '" << path << "'.";
+ return nullptr;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ LOG(ERROR) << "Path '" << path << "' is not a directory.";
+ return nullptr;
+ }
+
+ if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ path += OS_PATH_SEPARATOR;
+ }
+
+ return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
+ sb.st_mtime));
+}
+
+std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const {
+ const std::string resolved_path = dir_ + path;
+ if (file_exists != nullptr) {
+ struct stat sb{};
+ *file_exists = (stat(resolved_path.c_str(), &sb) != -1) && S_ISREG(sb.st_mode);
+ }
+
+ return CreateAssetFromFile(resolved_path);
+}
+
+bool DirectoryAssetsProvider::ForEachFile(
+ const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */)
+ const {
+ return true;
+}
+
+const std::string& DirectoryAssetsProvider::GetDebugName() const {
+ return dir_;
+}
+
+bool DirectoryAssetsProvider::IsUpToDate() const {
+ struct stat sb{};
+ if (stat(dir_.c_str(), &sb) < 0) {
+ // If stat fails on the zip archive, return true so the zip archive the resource system does
+ // attempt to refresh the ApkAsset.
+ return true;
+ }
+ return last_mod_time_ == sb.st_mtime;
+}
+
+MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary)
+ : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
+ secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+ if (primary_->GetDebugName() == kEmptyDebugString) {
+ debug_name_ = secondary_->GetDebugName();
+ } else if (secondary_->GetDebugName() == kEmptyDebugString) {
+ debug_name_ = primary_->GetDebugName();
+ } else {
+ debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
+ }
+}
+
+std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
+ std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
+ if (primary == nullptr || secondary == nullptr) {
+ return nullptr;
+ }
+ return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
+ std::move(secondary)));
+}
+
+std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const {
+ auto asset = primary_->Open(path, mode, file_exists);
+ return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
+}
+
+bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f)
+ const {
+ return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
+}
+
+const std::string& MultiAssetsProvider::GetDebugName() const {
+ return debug_name_;
+}
+
+bool MultiAssetsProvider::IsUpToDate() const {
+ return primary_->IsUpToDate() && secondary_->IsUpToDate();
+}
+
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
+ return std::make_unique<EmptyAssetsProvider>();
+}
+
+std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const {
+ if (file_exists) {
+ *file_exists = false;
+ }
+ return nullptr;
+}
+
+bool EmptyAssetsProvider::ForEachFile(
+ const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+}
+
+const std::string& EmptyAssetsProvider::GetDebugName() const {
+ const static std::string kEmpty = kEmptyDebugString;
+ return kEmpty;
+}
+
+bool EmptyAssetsProvider::IsUpToDate() const {
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index adb383f95..f216f55 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -257,8 +257,8 @@
target_apk_path_(target_apk_path),
idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
-std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data) {
+std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
+ const StringPiece& idmap_data) {
ATRACE_CALL();
size_t data_size = idmap_data.size();
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 996b424..2a70f0d 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -767,10 +767,10 @@
return true;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
- const size_t length,
- const LoadedIdmap* loaded_idmap,
- const package_property_t property_flags) {
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+ const size_t length,
+ const LoadedIdmap* loaded_idmap,
+ const package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
@@ -799,11 +799,10 @@
}
}
- // Need to force a move for mingw32.
- return std::move(loaded_arsc);
+ return loaded_arsc;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() {
+std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
return std::unique_ptr<LoadedArsc>(new LoadedArsc());
}
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index e57490a..d0019ed 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,104 +24,49 @@
#include "android-base/unique_fd.h"
#include "androidfw/Asset.h"
+#include "androidfw/AssetsProvider.h"
#include "androidfw/Idmap.h"
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
namespace android {
-class LoadedIdmap;
-
-// Interface for retrieving assets provided by an ApkAssets.
-class AssetsProvider {
- public:
- virtual ~AssetsProvider() = default;
-
- // Opens a file for reading.
- std::unique_ptr<Asset> Open(const std::string& path,
- Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
- bool* file_exists = nullptr) const {
- return OpenInternal(path, mode, file_exists);
- }
-
- // Iterate over all files and directories provided by the zip. The order of iteration is stable.
- virtual bool ForEachFile(const std::string& /* path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */) const {
- return true;
- }
-
- protected:
- AssetsProvider() = default;
-
- virtual std::unique_ptr<Asset> OpenInternal(const std::string& path,
- Asset::AccessMode mode,
- bool* file_exists) const = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AssetsProvider);
-};
-
-class ZipAssetsProvider;
-
// Holds an APK.
class ApkAssets {
public:
- // This means the data extends to the end of the file.
- static constexpr off64_t kUnknownLength = -1;
- // Creates an ApkAssets.
- // If `system` is true, the package is marked as a system package, and allows some functions to
- // filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+ // Creates an ApkAssets from a path on device.
+ static std::unique_ptr<ApkAssets> Load(const std::string& path,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
- // descriptor. The `friendly_name` is some name that will be used to identify the source of
- // this ApkAssets in log messages and other debug scenarios.
- // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read
- // using the `offset` into the file descriptor and will be `length` bytes long.
- static std::unique_ptr<const ApkAssets> LoadFromFd(
- base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
- off64_t length = kUnknownLength);
+ // Creates an ApkAssets from an open file descriptor.
+ static std::unique_ptr<ApkAssets> LoadFromFd(base::unique_fd fd,
+ const std::string& debug_name,
+ package_property_t flags = 0U,
+ off64_t offset = 0,
+ off64_t len = AssetsProvider::kUnknownLength);
- // Creates an ApkAssets from the given path which points to a resources.arsc.
- static std::unique_ptr<const ApkAssets> LoadTable(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+ // Creates an ApkAssets from an AssetProvider.
+ // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed.
+ static std::unique_ptr<ApkAssets> Load(std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and
- // takes ownership of the file descriptor.
- // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read
- // using the `offset` into the file descriptor and will be `length` bytes long.
- static std::unique_ptr<const ApkAssets> LoadTableFromFd(
- base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
- off64_t length = kUnknownLength);
+ // Creates an ApkAssets from the given asset file representing a resources.arsc.
+ static std::unique_ptr<ApkAssets> LoadTable(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags = 0U);
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
// data.
- static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path,
- package_property_t flags = 0U);
+ static std::unique_ptr<ApkAssets> LoadOverlay(const std::string& idmap_path,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the directory path. File-based resources are read within the
- // directory as if the directory is an APK.
- static std::unique_ptr<const ApkAssets> LoadFromDir(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
- // Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(
- package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
- const std::string& GetPath() const {
- return path_;
- }
+ // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same.
+ // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause
+ // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly
+ // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the
+ // same asset should have the same pointer.
+ const std::string& GetPath() const;
const AssetsProvider* GetAssetsProvider() const {
return assets_provider_.get();
@@ -146,53 +91,40 @@
// Returns whether the resources.arsc is allocated in RAM (not mmapped).
bool IsTableAllocated() const {
- return resources_asset_ && resources_asset_->isAllocated();
+ return resources_asset_ != nullptr && resources_asset_->isAllocated();
}
bool IsUpToDate() const;
- // Creates an Asset from a file on disk.
- static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
-
- // Creates an Asset from a file descriptor.
- //
- // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
- // must equal 0; otherwise, the asset data will be read using the `offset` into the file
- // descriptor and will be `length` bytes long.
- static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
- const char* path,
- off64_t offset = 0,
- off64_t length = kUnknownLength);
private:
- DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+ static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- static std::unique_ptr<const ApkAssets> LoadImpl(
- std::unique_ptr<const AssetsProvider> assets, const std::string& path,
- package_property_t property_flags,
- std::unique_ptr<const AssetsProvider> override_assets = nullptr,
- std::unique_ptr<Asset> idmap_asset = nullptr,
- std::unique_ptr<const LoadedIdmap> idmap = nullptr);
+ static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- static std::unique_ptr<const ApkAssets> LoadTableImpl(
- std::unique_ptr<Asset> resources_asset, const std::string& path,
- package_property_t property_flags,
- std::unique_ptr<const AssetsProvider> override_assets = nullptr);
+ ApkAssets(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<LoadedArsc> loaded_arsc,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
- std::string path,
- time_t last_mod_time,
- package_property_t property_flags);
-
- std::unique_ptr<const AssetsProvider> assets_provider_;
- const std::string path_;
- time_t last_mod_time_;
- package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
+ std::unique_ptr<LoadedArsc> loaded_arsc_;
+
+ std::unique_ptr<AssetsProvider> assets_provider_;
+ package_property_t property_flags_ = 0U;
+
std::unique_ptr<Asset> idmap_asset_;
- std::unique_ptr<const LoadedArsc> loaded_arsc_;
- std::unique_ptr<const LoadedIdmap> loaded_idmap_;
+ std::unique_ptr<LoadedIdmap> loaded_idmap_;
};
-} // namespace android
+} // namespace android
-#endif /* APKASSETS_H_ */
+#endif // APKASSETS_H_
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 80bae20..40c91a6 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -167,8 +167,8 @@
private:
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
- friend class ApkAssets;
- friend class ZipAssetsProvider;
+ friend struct ZipAssetsProvider;
+ friend struct AssetsProvider;
/*
* Create the asset from a named file on disk.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
new file mode 100644
index 0000000..7b06947
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROIDFW_ASSETSPROVIDER_H
+#define ANDROIDFW_ASSETSPROVIDER_H
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+#include "androidfw/misc.h"
+
+struct ZipArchive;
+
+namespace android {
+
+// Interface responsible for opening and iterating through asset files.
+struct AssetsProvider {
+ static constexpr off64_t kUnknownLength = -1;
+
+ // Opens a file for reading. If `file_exists` is not null, it will be set to `true` if the file
+ // exists. This is useful for determining if the file exists but was unable to be opened due to
+ // an I/O error.
+ std::unique_ptr<Asset> Open(const std::string& path,
+ Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
+ bool* file_exists = nullptr) const;
+
+ // Iterate over all files and directories provided by the interface. The order of iteration is
+ // stable.
+ virtual bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+
+ // Retrieves a name that represents the interface. This may or may not be the path of the
+ // interface source.
+ WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
+
+ // Returns whether the interface provides the most recent version of its files.
+ WARN_UNUSED virtual bool IsUpToDate() const = 0;
+
+ // Creates an Asset from a file on disk.
+ static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
+ // Creates an Asset from a file descriptor.
+ //
+ // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
+ // must equal 0; otherwise, the asset data will be read using the `offset` into the file
+ // descriptor and will be `length` bytes long.
+ static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset = 0,
+ off64_t length = AssetsProvider::kUnknownLength);
+
+ virtual ~AssetsProvider() = default;
+ protected:
+ virtual std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const = 0;
+};
+
+// Supplies assets from a zip archive.
+struct ZipAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path);
+ static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
+ std::string friendly_name,
+ off64_t offset = 0,
+ off64_t len = kUnknownLength);
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~ZipAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const override;
+
+ private:
+ struct PathOrDebugName;
+ ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time);
+
+ struct PathOrDebugName {
+ PathOrDebugName(std::string&& value, bool is_path);
+
+ // Retrieves the path or null if this class represents a debug name.
+ WARN_UNUSED const std::string* GetPath() const;
+
+ // Retrieves a name that represents the interface. This may or may not represent a path.
+ WARN_UNUSED const std::string& GetDebugName() const;
+
+ private:
+ std::string value_;
+ bool is_path_;
+ };
+
+ std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+ PathOrDebugName name_;
+ time_t last_mod_time_;
+};
+
+// Supplies assets from a root directory.
+struct DirectoryAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
+
+ bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~DirectoryAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const override;
+
+ private:
+ explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time);
+ std::string dir_;
+ time_t last_mod_time_;
+};
+
+// Supplies assets from a `primary` asset provider and falls back to supplying assets from the
+// `secondary` asset provider if the asset cannot be found in the `primary`.
+struct MultiAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary);
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~MultiAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override;
+
+ private:
+ MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary);
+
+ std::unique_ptr<AssetsProvider> primary_;
+ std::unique_ptr<AssetsProvider> secondary_;
+ std::string debug_name_;
+};
+
+// Does not provide any assets.
+struct EmptyAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<AssetsProvider> Create();
+
+ bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~EmptyAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const override;
+};
+
+} // namespace android
+
+#endif /* ANDROIDFW_ASSETSPROVIDER_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd9a8d13..0ded793 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -149,8 +149,8 @@
class LoadedIdmap {
public:
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
- static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data);
+ static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
+ const StringPiece& idmap_data);
// Returns the path to the IDMAP.
std::string_view IdmapPath() const {
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 891fb90..d9225cd 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -300,17 +300,14 @@
public:
// Load a resource table from memory pointed to by `data` of size `len`.
// The lifetime of `data` must out-live the LoadedArsc returned from this method.
- // If `system` is set to true, the LoadedArsc is considered as a system provided resource.
- // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
- // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
- // ID.
- static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
- size_t length,
- const LoadedIdmap* loaded_idmap = nullptr,
- package_property_t property_flags = 0U);
+
+ static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> data,
+ size_t length,
+ const LoadedIdmap* loaded_idmap = nullptr,
+ package_property_t property_flags = 0U);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
- static std::unique_ptr<const LoadedArsc> CreateEmpty();
+ static std::unique_ptr<LoadedArsc> CreateEmpty();
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 3f0c7cb..b434915 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -27,6 +27,8 @@
#include "data/overlayable/R.h"
#include "data/system/R.h"
+using ::testing::NotNull;
+
namespace overlay = com::android::overlay;
namespace overlayable = com::android::overlayable;
@@ -195,7 +197,11 @@
}
TEST_F(IdmapTest, OverlayLoaderInterop) {
- auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
+ auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ ASSERT_THAT(asset, NotNull());
+
+ auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
+ PROPERTY_LOADER);
AssetManager2 asset_manager;
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
overlay_assets_.get()});
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 9aa3634..f356c8130 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -339,10 +339,8 @@
}
TEST(LoadedArscTest, LoadCustomLoader) {
- std::string contents;
-
- std::unique_ptr<Asset>
- asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ ASSERT_THAT(asset, NotNull());
const StringPiece data(
reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 255c15c..eedb996 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -316,6 +316,15 @@
* Not guaranteed to be supported by devices, may be emulated if not supported. */
public static final int ENCODING_PCM_32BIT = 22;
+ /** Audio data format: MPEG-H baseline profile, level 3 */
+ public static final int ENCODING_MPEGH_BL_L3 = 23;
+ /** Audio data format: MPEG-H baseline profile, level 4 */
+ public static final int ENCODING_MPEGH_BL_L4 = 24;
+ /** Audio data format: MPEG-H low complexity profile, level 3 */
+ public static final int ENCODING_MPEGH_LC_L3 = 25;
+ /** Audio data format: MPEG-H low complexity profile, level 4 */
+ public static final int ENCODING_MPEGH_LC_L4 = 26;
+
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
switch(enc) {
@@ -363,6 +372,14 @@
return "ENCODING_PCM_24BIT_PACKED";
case ENCODING_PCM_32BIT:
return "ENCODING_PCM_32BIT";
+ case ENCODING_MPEGH_BL_L3:
+ return "ENCODING_MPEGH_BL_L3";
+ case ENCODING_MPEGH_BL_L4:
+ return "ENCODING_MPEGH_BL_L4";
+ case ENCODING_MPEGH_LC_L3:
+ return "ENCODING_MPEGH_LC_L3";
+ case ENCODING_MPEGH_LC_L4:
+ return "ENCODING_MPEGH_LC_L4";
default :
return "invalid encoding " + enc;
}
@@ -594,6 +611,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return true;
default:
return false;
@@ -625,6 +646,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return true;
default:
return false;
@@ -659,6 +684,10 @@
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
case ENCODING_OPUS:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return false;
case ENCODING_INVALID:
default:
@@ -693,6 +722,10 @@
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
case ENCODING_OPUS:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return false;
case ENCODING_INVALID:
default:
@@ -975,6 +1008,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1197,7 +1234,11 @@
ENCODING_DOLBY_MAT,
ENCODING_OPUS,
ENCODING_PCM_24BIT_PACKED,
- ENCODING_PCM_32BIT }
+ ENCODING_PCM_32BIT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
@@ -1213,6 +1254,10 @@
ENCODING_AC4,
ENCODING_E_AC3_JOC,
ENCODING_DOLBY_MAT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4,
};
/** @hide */
@@ -1225,7 +1270,11 @@
ENCODING_DOLBY_TRUEHD,
ENCODING_AC4,
ENCODING_E_AC3_JOC,
- ENCODING_DOLBY_MAT }
+ ENCODING_DOLBY_MAT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface SurroundSoundEncoding {}
@@ -1259,6 +1308,14 @@
return "Dolby Atmos in Dolby Digital Plus";
case ENCODING_DOLBY_MAT:
return "Dolby MAT";
+ case ENCODING_MPEGH_BL_L3:
+ return "MPEG-H 3D Audio baseline profile level 3";
+ case ENCODING_MPEGH_BL_L4:
+ return "MPEG-H 3D Audio baseline profile level 4";
+ case ENCODING_MPEGH_LC_L3:
+ return "MPEG-H 3D Audio low complexity profile level 3";
+ case ENCODING_MPEGH_LC_L4:
+ return "MPEG-H 3D Audio low complexity profile level 4";
default:
return "Unknown surround sound format";
}
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 65e75cd..d411831 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -83,9 +83,9 @@
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak tableta honetan."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 5563003..7556ace 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1484,5 +1484,5 @@
<!-- Content description of the Ethernet connection when disconnected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ethernet_disconnected">Ethernet disconnected.</string>
<!-- Content description of the Ethernet connection when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_ethernet_connected">Ethernet connected.</string>
+ <string name="accessibility_ethernet_connected">Ethernet.</string>
</resources>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index 10e28c4..d0c63a8 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -15,8 +15,10 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="180dp"
+ android:minWidth="140dp"
android:minHeight="40dp"
+ android:minResizeWidth="110dp"
+ android:minResizeHeight="40dp"
android:updatePeriodMillis="60000"
android:previewImage="@drawable/ic_android"
android:resizeMode="horizontal|vertical"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 7773fe9..75ef4b32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -128,6 +128,8 @@
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() != View.VISIBLE) continue;
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1b2ad4c..191b85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -54,6 +54,9 @@
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.WifiIcons;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/** Quick settings tile: Internet **/
@@ -65,7 +68,7 @@
protected final NetworkController mController;
private final DataUsageController mDataController;
private final QSTile.SignalState mStateBeforeClick = newTileState();
- // The last updated tile state, 0: mobile, 1: wifi
+ // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
private int mLastTileState = -1;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
@@ -140,6 +143,21 @@
return string;
}
+ private static final class EthernetCallbackInfo {
+ boolean mConnected;
+ int mEthernetSignalIconId;
+ String mEthernetContentDescription;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("EthernetCallbackInfo[")
+ .append("mConnected=").append(mConnected)
+ .append(",mEthernetSignalIconId=").append(mEthernetSignalIconId)
+ .append(",mEthernetContentDescription=").append(mEthernetContentDescription)
+ .append(']').toString();
+ }
+ }
+
private static final class WifiCallbackInfo {
boolean mAirplaneModeEnabled;
boolean mEnabled;
@@ -212,6 +230,8 @@
protected final class InternetSignalCallback implements SignalCallback {
final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
+ final EthernetCallbackInfo mEthernetInfo = new EthernetCallbackInfo();
+
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -230,14 +250,12 @@
}
// When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi
// is not the default network.
- if (qsIcon == null && !mWifiInfo.mAirplaneModeEnabled) {
+ if (qsIcon == null) {
return;
}
- if (qsIcon != null) {
- mWifiInfo.mConnected = qsIcon.visible;
- mWifiInfo.mWifiSignalIconId = qsIcon.icon;
- mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
- }
+ mWifiInfo.mConnected = qsIcon.visible;
+ mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+ mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
mWifiInfo.mEnabled = enabled;
mWifiInfo.mSsid = description;
mWifiInfo.mActivityIn = activityIn;
@@ -287,6 +305,20 @@
}
@Override
+ public void setEthernetIndicators(IconState icon) {
+ if (DEBUG) {
+ Log.d(TAG, "setEthernetIndicators: "
+ + "icon = " + (icon == null ? "" : icon.toString()));
+ }
+ mEthernetInfo.mConnected = icon.visible;
+ mEthernetInfo.mEthernetSignalIconId = icon.icon;
+ mEthernetInfo.mEthernetContentDescription = icon.contentDescription;
+ if (icon.visible) {
+ refreshState(mEthernetInfo);
+ }
+ }
+
+ @Override
public void setNoSims(boolean show, boolean simDetected) {
if (DEBUG) {
Log.d(TAG, "setNoSims: "
@@ -299,7 +331,6 @@
mCellularInfo.mMobileSignalIconId = 0;
mCellularInfo.mQsTypeIcon = 0;
}
- refreshState(mCellularInfo);
}
@Override
@@ -310,7 +341,9 @@
}
mCellularInfo.mAirplaneModeEnabled = icon.visible;
mWifiInfo.mAirplaneModeEnabled = icon.visible;
- refreshState(mCellularInfo);
+ if (!mSignalCallback.mEthernetInfo.mConnected) {
+ refreshState(mCellularInfo);
+ }
}
@Override
@@ -330,6 +363,15 @@
mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
refreshState(mWifiInfo);
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder("InternetSignalCallback[")
+ .append("mWifiInfo=").append(mWifiInfo)
+ .append(",mCellularInfo=").append(mCellularInfo)
+ .append(",mEthernetInfo=").append(mEthernetInfo)
+ .append(']').toString();
+ }
}
@Override
@@ -340,6 +382,9 @@
} else if (arg instanceof WifiCallbackInfo) {
mLastTileState = 1;
handleUpdateWifiState(state, arg);
+ } else if (arg instanceof EthernetCallbackInfo) {
+ mLastTileState = 2;
+ handleUpdateEthernetState(state, arg);
} else {
// handleUpdateState will be triggered when user expands the QuickSetting panel with
// arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
@@ -348,6 +393,8 @@
handleUpdateCellularState(state, mSignalCallback.mCellularInfo);
} else if (mLastTileState == 1) {
handleUpdateWifiState(state, mSignalCallback.mWifiInfo);
+ } else if (mLastTileState == 2) {
+ handleUpdateEthernetState(state, mSignalCallback.mEthernetInfo);
}
}
}
@@ -440,7 +487,6 @@
Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
}
final Resources r = mContext.getResources();
- // TODO(b/174753536): Use the new "Internet" string as state.label once available.
state.label = r.getString(R.string.quick_settings_internet_label);
state.state = Tile.STATE_ACTIVE;
boolean mobileDataEnabled = mDataController.isMobileDataSupported()
@@ -478,6 +524,18 @@
}
}
+ private void handleUpdateEthernetState(SignalState state, Object arg) {
+ EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
+ }
+ final Resources r = mContext.getResources();
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ state.state = Tile.STATE_ACTIVE;
+ state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
+ state.secondaryLabel = cb.mEthernetContentDescription;
+ }
+
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
if (TextUtils.isEmpty(dataType)) {
return Html.fromHtml((current == null ? "" : current.toString()), 0);
@@ -519,4 +577,15 @@
return d;
}
}
+
+ /**
+ * Dumps the state of this tile along with its name.
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(this.getClass().getSimpleName() + ":");
+ pw.print(" "); pw.println(getState().toString());
+ pw.print(" "); pw.println("mLastTileState=" + mLastTileState);
+ pw.print(" "); pw.println("mSignalCallback=" + mSignalCallback.toString());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 0a7eea4..953b40b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -388,12 +388,6 @@
}
});
- // ignore system bar insets for the purpose of window layout
- mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets)
- .setInsets(WindowInsets.Type.all(), Insets.NONE)
- .build()));
-
// TODO(159460485): Remove this when focus is handled properly in the system
mScreenshotView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
@@ -533,9 +527,6 @@
attachWindow();
- if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
- }
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -549,7 +540,13 @@
}
});
mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
setContentView(mScreenshotView);
+ // ignore system bar insets for the purpose of window layout
+ mWindow.getDecorView().setOnApplyWindowInsetsListener(
+ (v, insets) -> WindowInsets.CONSUMED);
cancelTimeout(); // restarted after animation
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index 2eff04e..80b75a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -53,13 +53,21 @@
public void notifyListeners(SignalCallback callback) {
boolean ethernetVisible = mCurrentState.connected;
String contentDescription = getTextIfExists(getContentDescription()).toString();
-
// TODO: wire up data transfer using WifiSignalPoller.
callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
contentDescription));
}
@Override
+ public int getContentDescription() {
+ if (mCurrentState.connected) {
+ return getIcons().contentDesc[1];
+ } else {
+ return getIcons().discContentDesc;
+ }
+ }
+
+ @Override
public State cleanState() {
return new State();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f9450ae..e13e30b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -524,6 +524,10 @@
return mWifiSignalController.isCarrierMergedWifi(subId);
}
+ boolean isEthernetDefault() {
+ return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+ }
+
String getNonDefaultMobileDataNetworkName(int subId) {
MobileSignalController controller = getControllerWithSubId(subId);
return controller != null ? controller.getNonDefaultCarrierName() : "";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 9669522..b2120d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -106,7 +106,8 @@
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
if (mProviderModel) {
IconState qsIcon = null;
- if (mCurrentState.isDefault) {
+ if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+ && !mNetworkController.isEthernetDefault())) {
qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
: getQsCurrentIconId(), contentDescription);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
index 7824fd9..9c54100 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -44,7 +44,7 @@
@Override
protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mCompletedTapCount + 1 == mTargetFingerCount) {
+ if (mCompletedTapCount + 1 == mTargetTapCount) {
// Calling super.onUp would complete the multi-tap version of this.
cancelGesture(event, rawEvent, policyFlags);
} else {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6216fc0..7da4d64 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -671,34 +671,29 @@
}
if (fgRequired) {
- if (isFgsBgStart(r.mAllowStartForeground)) {
- if (!r.mLoggedInfoAllowStartForeground) {
- Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground);
- r.mLoggedInfoAllowStartForeground = true;
+ logFgsBackgroundStart(r);
+ if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
+ String msg = "startForegroundService() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName;
+ Slog.w(TAG, msg);
+ showFgsBgRestrictedNotificationLocked(r);
+ ApplicationInfo aInfo = null;
+ try {
+ aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+ callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
+ userId);
+ } catch (android.os.RemoteException e) {
+ // pm is in same process, this will never happen.
}
- if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
- String msg = "startForegroundService() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName;
- Slog.w(TAG, msg);
- showFgsBgRestrictedNotificationLocked(r);
- ApplicationInfo aInfo = null;
- try {
- aInfo = AppGlobals.getPackageManager().getApplicationInfo(
- callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
- userId);
- } catch (android.os.RemoteException e) {
- // pm is in same process, this will never happen.
- }
- if (aInfo == null) {
- throw new SecurityException("startServiceLocked failed, "
- + "could not resolve client package " + callingPackage);
- }
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
- throw new IllegalStateException(msg);
- }
- return null;
+ if (aInfo == null) {
+ throw new SecurityException("startServiceLocked failed, "
+ + "could not resolve client package " + callingPackage);
}
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+ throw new IllegalStateException(msg);
+ }
+ return null;
}
}
@@ -1768,25 +1763,19 @@
}
if (!ignoreForeground) {
- if (isFgsBgStart(r.mAllowStartForeground)) {
- if (!r.mLoggedInfoAllowStartForeground) {
- Slog.wtf(TAG, "Background started FGS "
- + r.mInfoAllowStartForeground);
- r.mLoggedInfoAllowStartForeground = true;
- }
- if (r.mAllowStartForeground == FGS_FEATURE_DENIED
- && isBgFgsRestrictionEnabled(r)) {
- final String msg = "Service.startForeground() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName;
- Slog.w(TAG, msg);
- showFgsBgRestrictedNotificationLocked(r);
- updateServiceForegroundLocked(r.app, true);
- ignoreForeground = true;
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
- r.appInfo.uid)) {
- throw new IllegalStateException(msg);
- }
+ logFgsBackgroundStart(r);
+ if (r.mAllowStartForeground == FGS_FEATURE_DENIED
+ && isBgFgsRestrictionEnabled(r)) {
+ final String msg = "Service.startForeground() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName;
+ Slog.w(TAG, msg);
+ showFgsBgRestrictedNotificationLocked(r);
+ updateServiceForegroundLocked(r.app, true);
+ ignoreForeground = true;
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
+ r.appInfo.uid)) {
+ throw new IllegalStateException(msg);
}
}
}
@@ -5732,4 +5721,23 @@
return mAm.mConstants.mFlagFgsStartRestrictionEnabled
&& CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
}
+
+ private void logFgsBackgroundStart(ServiceRecord r) {
+ // Only log if FGS is started from background.
+ if (!isFgsBgStart(r.mAllowStartForeground)) {
+ return;
+ }
+ if (!r.mLoggedInfoAllowStartForeground) {
+ final String msg = "Background started FGS: "
+ + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ")
+ + r.mInfoAllowStartForeground;
+ Slog.wtfQuiet(TAG, msg);
+ if (r.mAllowStartForeground != FGS_FEATURE_DENIED) {
+ Slog.i(TAG, msg);
+ } else {
+ Slog.w(TAG, msg);
+ }
+ r.mLoggedInfoAllowStartForeground = true;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c8f5f8e..cdf5c98 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5909,18 +5909,10 @@
abiOverride, zygotePolicyFlags);
}
- @GuardedBy("this")
- final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
- return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
- false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags);
- }
-
// TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- String abiOverride, int zygotePolicyFlags) {
+ boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -5955,7 +5947,7 @@
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
- zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride);
+ zygotePolicyFlags, disableHiddenApiChecks, abiOverride);
}
return app;
@@ -13349,11 +13341,12 @@
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
boolean disableTestApiChecks = disableHiddenApiChecks
|| (flags & INSTR_FLAG_DISABLE_TEST_API_CHECKS) != 0;
+
if (disableHiddenApiChecks || disableTestApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
"disable hidden API checks");
- enableTestApiAccess(ii.packageName);
+ enableTestApiAccess(ai.packageName);
}
final long origId = Binder.clearCallingIdentity();
@@ -13370,11 +13363,10 @@
mUsageStatsService.reportEvent(ii.targetPackage, userId,
UsageEvents.Event.SYSTEM_INTERACTION);
}
- app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
+ app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
+ ZYGOTE_POLICY_FLAG_EMPTY);
}
-
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mSourceUid = callingUid;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bbf927b..8f99459 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1767,8 +1767,7 @@
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- String abiOverride) {
+ int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) {
if (app.pendingStart) {
return true;
}
@@ -1914,10 +1913,6 @@
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
-
- if (disableTestApiChecks) {
- runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
- }
}
String useAppImageCache = SystemProperties.get(
@@ -2367,8 +2362,7 @@
final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, String abiOverride) {
return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
- false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
- abiOverride);
+ false /* disableHiddenApiChecks */, abiOverride);
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 8e5215b..9036812 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -26,7 +26,6 @@
import android.graphics.fonts.FontManager;
import android.graphics.fonts.SystemFonts;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
@@ -67,13 +66,12 @@
private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";
@Override
- public FontConfig getFontConfig() throws RemoteException {
+ public FontConfig getFontConfig() {
return getSystemFontConfig();
}
@Override
- public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion)
- throws RemoteException {
+ public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion) {
Objects.requireNonNull(fd);
Objects.requireNonNull(signature);
Preconditions.checkArgumentNonnegative(baseVersion);
@@ -183,14 +181,21 @@
@NonNull
private final Context mContext;
- @GuardedBy("FontManagerService.this")
+ private final Object mUpdatableFontDirLock = new Object();
+
+ @GuardedBy("mUpdatableFontDirLock")
@NonNull
private final FontCrashDetector mFontCrashDetector;
+ @GuardedBy("mUpdatableFontDirLock")
@Nullable
private final UpdatableFontDir mUpdatableFontDir;
- @GuardedBy("FontManagerService.this")
+ // mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock.
+ // mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock.
+ private final Object mSerializedFontMapLock = new Object();
+
+ @GuardedBy("mSerializedFontMapLock")
@Nullable
private SharedMemory mSerializedFontMap = null;
@@ -212,9 +217,9 @@
}
private void initialize() {
- synchronized (FontManagerService.this) {
+ synchronized (mUpdatableFontDirLock) {
if (mUpdatableFontDir == null) {
- mSerializedFontMap = buildNewSerializedFontMap();
+ updateSerializedFontMap();
return;
}
if (mFontCrashDetector.hasCrashed()) {
@@ -228,7 +233,7 @@
}
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
mUpdatableFontDir.loadFontFileMap();
- mSerializedFontMap = buildNewSerializedFontMap();
+ updateSerializedFontMap();
}
}
}
@@ -239,7 +244,7 @@
}
@Nullable /* package */ SharedMemory getCurrentFontMap() {
- synchronized (FontManagerService.this) {
+ synchronized (mSerializedFontMapLock) {
return mSerializedFontMap;
}
}
@@ -251,7 +256,7 @@
FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- synchronized (FontManagerService.this) {
+ synchronized (mUpdatableFontDirLock) {
// baseVersion == -1 only happens from shell command. This is filtered and treated as
// error from SystemApi call.
if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) {
@@ -261,7 +266,7 @@
}
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
- mSerializedFontMap = buildNewSerializedFontMap();
+ updateSerializedFontMap();
}
}
}
@@ -272,10 +277,10 @@
FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- synchronized (FontManagerService.this) {
+ synchronized (mUpdatableFontDirLock) {
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
mUpdatableFontDir.clearUpdates();
- mSerializedFontMap = buildNewSerializedFontMap();
+ updateSerializedFontMap();
}
}
}
@@ -283,7 +288,8 @@
/* package */ Map<String, File> getFontFileMap() {
if (mUpdatableFontDir == null) {
return Collections.emptyMap();
- } else {
+ }
+ synchronized (mUpdatableFontDirLock) {
return mUpdatableFontDir.getFontFileMap();
}
}
@@ -301,7 +307,7 @@
@Nullable FileDescriptor err,
@NonNull String[] args,
@Nullable ShellCallback callback,
- @NonNull ResultReceiver result) throws RemoteException {
+ @NonNull ResultReceiver result) {
new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
@@ -309,24 +315,28 @@
* Returns an active system font configuration.
*/
public @NonNull FontConfig getSystemFontConfig() {
- if (mUpdatableFontDir != null) {
- return mUpdatableFontDir.getSystemFontConfig();
- } else {
+ if (mUpdatableFontDir == null) {
return SystemFonts.getSystemPreinstalledFontConfig();
}
+ synchronized (mUpdatableFontDirLock) {
+ return mUpdatableFontDir.getSystemFontConfig();
+ }
}
/**
- * Make new serialized font map data.
+ * Makes new serialized font map data and updates mSerializedFontMap.
*/
- public @Nullable SharedMemory buildNewSerializedFontMap() {
+ public void updateSerializedFontMap() {
try {
final FontConfig fontConfig = getSystemFontConfig();
final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
- return Typeface.serializeFontMap(typefaceMap);
+ SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+ synchronized (mSerializedFontMapLock) {
+ mSerializedFontMap = serializeFontMap;
+ }
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
@@ -338,11 +348,13 @@
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
- return Typeface.serializeFontMap(typefaceMap);
+ SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+ synchronized (mSerializedFontMapLock) {
+ mSerializedFontMap = serializeFontMap;
+ }
} catch (IOException | ErrnoException e) {
Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
}
- return null;
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 0cb7045..8fac086 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -29,8 +29,6 @@
import android.util.Base64;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -44,6 +42,11 @@
import java.util.List;
import java.util.Map;
+/**
+ * Manages set of updatable font files.
+ *
+ * <p>This class is not thread safe.
+ */
final class UpdatableFontDir {
private static final String TAG = "UpdatableFontDir";
@@ -113,11 +116,9 @@
private final File mConfigFile;
private final File mTmpConfigFile;
- @GuardedBy("UpdatableFontDir.this")
private final PersistentSystemFontConfig.Config mConfig =
new PersistentSystemFontConfig.Config();
- @GuardedBy("UpdatableFontDir.this")
private int mConfigVersion = 1;
/**
@@ -125,7 +126,6 @@
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
* corresponding font files in {@link #mPreinstalledFontDirs}.
*/
- @GuardedBy("UpdatableFontDir.this")
private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
@@ -145,57 +145,53 @@
}
/* package */ void loadFontFileMap() {
- synchronized (UpdatableFontDir.this) {
- boolean success = false;
+ boolean success = false;
- try (FileInputStream fis = new FileInputStream(mConfigFile)) {
- PersistentSystemFontConfig.loadFromXml(fis, mConfig);
- } catch (IOException | XmlPullParserException e) {
- mConfig.reset();
+ try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+ PersistentSystemFontConfig.loadFromXml(fis, mConfig);
+ } catch (IOException | XmlPullParserException e) {
+ mConfig.reset();
+ }
+
+ mFontFileInfoMap.clear();
+ try {
+ File[] dirs = mFilesDir.listFiles();
+ if (dirs == null) return;
+ for (File dir : dirs) {
+ if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
+ File[] files = dir.listFiles();
+ if (files == null || files.length != 1) return;
+ FontFileInfo fontFileInfo = validateFontFile(files[0]);
+ addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
}
-
- mFontFileInfoMap.clear();
- try {
- File[] dirs = mFilesDir.listFiles();
- if (dirs == null) return;
- for (File dir : dirs) {
- if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
- File[] files = dir.listFiles();
- if (files == null || files.length != 1) return;
- FontFileInfo fontFileInfo = validateFontFile(files[0]);
- addFileToMapIfNewerLocked(fontFileInfo, true /* deleteOldFile */);
- }
- success = true;
- } catch (Throwable t) {
- // If something happened during loading system fonts, clear all contents in finally
- // block. Here, just dumping errors.
- Slog.e(TAG, "Failed to load font mappings.", t);
- } finally {
- // Delete all files just in case if we find a problematic file.
- if (!success) {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
- }
+ success = true;
+ } catch (Throwable t) {
+ // If something happened during loading system fonts, clear all contents in finally
+ // block. Here, just dumping errors.
+ Slog.e(TAG, "Failed to load font mappings.", t);
+ } finally {
+ // Delete all files just in case if we find a problematic file.
+ if (!success) {
+ mFontFileInfoMap.clear();
+ FileUtils.deleteContents(mFilesDir);
}
}
}
/* package */ void clearUpdates() throws SystemFontException {
- synchronized (UpdatableFontDir.this) {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
+ mFontFileInfoMap.clear();
+ FileUtils.deleteContents(mFilesDir);
- mConfig.reset();
- mConfig.lastModifiedDate = Instant.now().getEpochSecond();
- try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, mConfig);
- } catch (Exception e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
- "Failed to write config XML.", e);
- }
- mConfigVersion++;
+ mConfig.reset();
+ mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, mConfig);
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to write config XML.", e);
}
+ mConfigVersion++;
}
/**
@@ -210,110 +206,108 @@
* @throws SystemFontException if error occurs.
*/
void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
- synchronized (UpdatableFontDir.this) {
- File newDir = getRandomDir(mFilesDir);
- if (!newDir.mkdir()) {
+ File newDir = getRandomDir(mFilesDir);
+ if (!newDir.mkdir()) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to create font directory.");
+ }
+ try {
+ // Make newDir executable so that apps can access font file inside newDir.
+ Os.chmod(newDir.getAbsolutePath(), 0711);
+ } catch (ErrnoException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to change mode to 711", e);
+ }
+ boolean success = false;
+ try {
+ File tempNewFontFile = new File(newDir, "font.ttf");
+ try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
+ FileUtils.copy(fd, out.getFD());
+ } catch (IOException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to create font directory.");
+ "Failed to write font file to storage.", e);
}
try {
- // Make newDir executable so that apps can access font file inside newDir.
- Os.chmod(newDir.getAbsolutePath(), 0711);
+ // Do not parse font file before setting up fs-verity.
+ // setUpFsverity throws IOException if failed.
+ mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
+ pkcs7Signature);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
+ "Failed to setup fs-verity.", e);
+ }
+ String postScriptName;
+ try {
+ postScriptName = mParser.getPostScriptName(tempNewFontFile);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_FONT_FILE,
+ "Failed to read PostScript name from font file", e);
+ }
+ if (postScriptName == null) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_FONT_NAME,
+ "Failed to read PostScript name from font file");
+ }
+ File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+ if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to move verified font file.");
+ }
+ try {
+ // Make the font file readable by apps.
+ Os.chmod(newFontFile.getAbsolutePath(), 0644);
} catch (ErrnoException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
"Failed to change mode to 711", e);
}
- boolean success = false;
- try {
- File tempNewFontFile = new File(newDir, "font.ttf");
- try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
- FileUtils.copy(fd, out.getFD());
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to write font file to storage.", e);
- }
- try {
- // Do not parse font file before setting up fs-verity.
- // setUpFsverity throws IOException if failed.
- mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
- pkcs7Signature);
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
- "Failed to setup fs-verity.", e);
- }
- String postScriptName;
- try {
- postScriptName = mParser.getPostScriptName(tempNewFontFile);
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_INVALID_FONT_FILE,
- "Failed to read PostScript name from font file", e);
- }
- if (postScriptName == null) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_INVALID_FONT_NAME,
- "Failed to read PostScript name from font file");
- }
- File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
- if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to move verified font file.");
- }
- try {
- // Make the font file readable by apps.
- Os.chmod(newFontFile.getAbsolutePath(), 0644);
- } catch (ErrnoException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to change mode to 711", e);
- }
- FontFileInfo fontFileInfo = validateFontFile(newFontFile);
+ FontFileInfo fontFileInfo = validateFontFile(newFontFile);
- // Write config file.
- PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
- mConfig.copyTo(copied);
+ // Write config file.
+ PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
+ mConfig.copyTo(copied);
- copied.lastModifiedDate = Instant.now().getEpochSecond();
- try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, copied);
- } catch (Exception e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
- "Failed to write config XML.", e);
- }
+ copied.lastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, copied);
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to write config XML.", e);
+ }
- // Backup the mapping for rollback.
- HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
- if (!addFileToMapIfNewerLocked(fontFileInfo, false)) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_DOWNGRADING,
- "Downgrading font file is forbidden.");
- }
+ // Backup the mapping for rollback.
+ HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
+ if (!addFileToMapIfNewer(fontFileInfo, false)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_DOWNGRADING,
+ "Downgrading font file is forbidden.");
+ }
- if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
- // If we fail to stage the config file, need to rollback the config.
- mFontFileInfoMap.clear();
- mFontFileInfoMap.putAll(backup);
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
- "Failed to stage the config file.");
- }
+ if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+ // If we fail to stage the config file, need to rollback the config.
+ mFontFileInfoMap.clear();
+ mFontFileInfoMap.putAll(backup);
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to stage the config file.");
+ }
- // Now font update is succeeded. Update config version.
- copied.copyTo(mConfig);
- mConfigVersion++;
+ // Now font update is succeeded. Update config version.
+ copied.copyTo(mConfig);
+ mConfigVersion++;
- success = true;
- } finally {
- if (!success) {
- FileUtils.deleteContentsAndDir(newDir);
- }
+ success = true;
+ } finally {
+ if (!success) {
+ FileUtils.deleteContentsAndDir(newDir);
}
}
}
@@ -341,7 +335,7 @@
* higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
* #mPreinstalledFontDirs}).
*/
- private boolean addFileToMapIfNewerLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+ private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
String name = fontFileInfo.getFile().getName();
FontFileInfo existingInfo = mFontFileInfoMap.get(name);
final boolean shouldAddToMap;
@@ -447,27 +441,21 @@
Map<String, File> getFontFileMap() {
Map<String, File> map = new HashMap<>();
- synchronized (UpdatableFontDir.this) {
- for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
- map.put(entry.getKey(), entry.getValue().getFile());
- }
+ for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
+ map.put(entry.getKey(), entry.getValue().getFile());
}
return map;
}
/* package */ FontConfig getSystemFontConfig() {
- synchronized (UpdatableFontDir.this) {
- return SystemFonts.getSystemFontConfig(
- getFontFileMap(),
- mConfig.lastModifiedDate,
- mConfigVersion
- );
- }
+ return SystemFonts.getSystemFontConfig(
+ getFontFileMap(),
+ mConfig.lastModifiedDate,
+ mConfigVersion
+ );
}
/* package */ int getConfigVersion() {
- synchronized (UpdatableFontDir.this) {
- return mConfigVersion;
- }
+ return mConfigVersion;
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 38ae51f..83085cc 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -977,7 +977,7 @@
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
List<AudioPortConfig> sinkConfigs = new ArrayList<>();
AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
- boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+ boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null;
for (AudioDevicePort audioSink : mAudioSink) {
AudioPortConfig sinkConfig = audioSink.activeConfig();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ed97848..1b20c44 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -168,7 +168,6 @@
import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
-import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6a86aee..48e4df7 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -69,7 +69,6 @@
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -253,7 +252,7 @@
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
- mOrientationListener = new OrientationListener(mContext, uiHandler);
+ mOrientationListener = new OrientationListener(mContext, uiHandler, mService);
mOrientationListener.setCurrentRotation(mRotation);
mSettingsObserver = new SettingsObserver(uiHandler);
mSettingsObserver.observe();
@@ -1474,8 +1473,8 @@
final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
boolean mEnabled;
- OrientationListener(Context context, Handler handler) {
- super(context, handler);
+ OrientationListener(Context context, Handler handler, WindowManagerService service) {
+ super(context, handler, service);
}
private class UpdateRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ScreenshotHashController.java
similarity index 79%
rename from services/core/java/com/android/server/wm/ImpressionAttestationController.java
rename to services/core/java/com/android/server/wm/ScreenshotHashController.java
index bad6c80..03f4e28 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ScreenshotHashController.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
-import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS;
+import static android.service.screenshot.ScreenshotHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -44,9 +46,9 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.attestation.IImpressionAttestationService;
-import android.service.attestation.ImpressionAttestationService;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.IScreenshotHasherService;
+import android.service.screenshot.ScreenshotHash;
+import android.service.screenshot.ScreenshotHasherService;
import android.util.Slog;
import android.view.MagnificationSpec;
@@ -59,30 +61,29 @@
import java.util.function.BiConsumer;
/**
- * Handles requests into {@link ImpressionAttestationService}
+ * Handles requests into {@link android.service.screenshot.ScreenshotHasherService}
*
* Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
* blocking calls into another service.
*/
-public class ImpressionAttestationController {
- private static final String TAG =
- TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM;
+public class ScreenshotHashController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM;
private static final boolean DEBUG = false;
private final Object mServiceConnectionLock = new Object();
@GuardedBy("mServiceConnectionLock")
- private ImpressionAttestationServiceConnection mServiceConnection;
+ private ScreenshotHasherServiceConnection mServiceConnection;
private final Context mContext;
/**
- * Lock used for the cached {@link #mImpressionAlgorithms} array
+ * Lock used for the cached {@link #mHashingAlgorithms} array
*/
- private final Object mImpressionAlgorithmsLock = new Object();
+ private final Object mHashingAlgorithmsLock = new Object();
- @GuardedBy("mImpressionAlgorithmsLock")
- private String[] mImpressionAlgorithms;
+ @GuardedBy("mHashingAlgorithmsLock")
+ private String[] mHashingAlgorithms;
private final Handler mHandler;
@@ -93,24 +94,24 @@
private final RectF mTmpRectF = new RectF();
private interface Command {
- void run(IImpressionAttestationService service) throws RemoteException;
+ void run(IScreenshotHasherService service) throws RemoteException;
}
- ImpressionAttestationController(Context context) {
+ ScreenshotHashController(Context context) {
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
mSalt = UUID.randomUUID().toString().getBytes();
}
- String[] getSupportedImpressionAlgorithms() {
- // We have a separate lock for the impression algorithm array since it doesn't need to make
+ String[] getSupportedHashingAlgorithms() {
+ // We have a separate lock for the hashing algorithm array since it doesn't need to make
// the request through the service connection. Instead, we have a lock to ensure we can
- // properly cache the impression algorithms array so we don't need to call into the
+ // properly cache the hashing algorithms array so we don't need to call into the
// ExtServices process for each request.
- synchronized (mImpressionAlgorithmsLock) {
+ synchronized (mHashingAlgorithmsLock) {
// Already have cached values
- if (mImpressionAlgorithms != null) {
- return mImpressionAlgorithms;
+ if (mHashingAlgorithms != null) {
+ return mHashingAlgorithms;
}
final ServiceInfo serviceInfo = getServiceInfo();
@@ -127,54 +128,55 @@
final int resourceId = serviceInfo.metaData.getInt(
SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
- mImpressionAlgorithms = res.getStringArray(resourceId);
+ mHashingAlgorithms = res.getStringArray(resourceId);
- return mImpressionAlgorithms;
+ return mHashingAlgorithms;
}
}
- boolean verifyImpressionToken(ImpressionToken impressionToken) {
+ boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.verifyImpressionToken(mSalt, impressionToken, remoteCallback);
+ service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke verifyImpressionToken command");
+ Slog.e(TAG, "Failed to invoke verifyScreenshotHash command");
}
});
- return results.getBoolean(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS);
+ return results.getBoolean(EXTRA_VERIFICATION_STATUS);
}
- ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+ ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds,
String hashAlgorithm) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.generateImpressionToken(mSalt, screenshot, bounds, hashAlgorithm,
+ service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm,
remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke generateImpressionToken command", e);
+ Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e);
}
});
- return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN);
+ return results.getParcelable(EXTRA_SCREENSHOT_HASH);
}
/**
- * Calculate the bounds to take the screenshot when generating the impression token. This takes
- * into account window transform, magnification, and display bounds.
+ * Calculate the bounds to take the screenshot when generating the ScreenshotHash. This
+ * takes into account window transform, magnification, and display bounds.
*
* Call while holding {@link WindowManagerService#mGlobalLock}
*
- * @param win Window that the impression token is generated for.
+ * @param win Window that the ScreenshotHash is generated for.
* @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
* @param outBounds The result of the calculated bounds
*/
- void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow,
+ void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow,
Rect outBounds) {
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow);
+ Slog.d(TAG,
+ "calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow);
}
outBounds.set(boundsInWindow);
@@ -192,7 +194,8 @@
outBounds.intersectUnchecked(windowBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds);
+ Slog.d(TAG,
+ "calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds);
}
if (outBounds.isEmpty()) {
@@ -207,7 +210,7 @@
outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
(int) mTmpRectF.bottom);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds);
}
// Apply the magnification spec values to the bounds since the content could be magnified
@@ -218,7 +221,8 @@
}
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification="
+ + outBounds);
}
if (outBounds.isEmpty()) {
@@ -230,7 +234,7 @@
final Rect displayBounds = displayContent.getBounds();
outBounds.intersectUnchecked(displayBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds);
}
}
@@ -244,7 +248,7 @@
if (DEBUG) Slog.v(TAG, "creating connection");
// Create the connection
- mServiceConnection = new ImpressionAttestationServiceConnection();
+ mServiceConnection = new ScreenshotHasherServiceConnection();
final ComponentName component = getServiceComponentName();
if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -275,7 +279,7 @@
return null;
}
- final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE);
+ final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE);
intent.setPackage(packageName);
final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -292,10 +296,10 @@
if (serviceInfo == null) return null;
final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE
+ if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " requires permission "
- + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE);
+ + Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE);
return null;
}
@@ -308,7 +312,7 @@
private Bundle mResult;
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) {
+ public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) {
connectAndRun(service -> {
RemoteCallback callback = new RemoteCallback(result -> {
mResult = result;
@@ -327,9 +331,9 @@
}
}
- private class ImpressionAttestationServiceConnection implements ServiceConnection {
+ private class ScreenshotHasherServiceConnection implements ServiceConnection {
@GuardedBy("mServiceConnectionLock")
- private IImpressionAttestationService mRemoteService;
+ private IScreenshotHasherService mRemoteService;
@GuardedBy("mServiceConnectionLock")
private ArrayList<Command> mQueuedCommands;
@@ -338,7 +342,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
synchronized (mServiceConnectionLock) {
- mRemoteService = IImpressionAttestationService.Stub.asInterface(service);
+ mRemoteService = IScreenshotHasherService.Stub.asInterface(service);
if (mQueuedCommands != null) {
final int size = mQueuedCommands.size();
if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 8d8bdcb..fe7ec16 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -57,7 +57,7 @@
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.MergedConfiguration;
@@ -862,11 +862,11 @@
}
@Override
- public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+ public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
final long origId = Binder.clearCallingIdentity();
try {
- return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm);
+ return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9a..015a0fb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -49,6 +49,10 @@
static final String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
"system_gesture_exclusion_log_debounce_millis";
+ // Enable logging from the sensor which publishes accel and gyro data generating a rotation
+ // event
+ private static final String KEY_RAW_SENSOR_LOGGING_ENABLED = "raw_sensor_logging_enabled";
+
private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
/** @see #KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS */
@@ -58,6 +62,8 @@
/** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
boolean mSystemGestureExcludedByPreQStickyImmersive;
+ boolean mRawSensorLoggingEnabled;
+
private final WindowManagerGlobalLock mGlobalLock;
private final Runnable mUpdateSystemGestureExclusionCallback;
private final DeviceConfigInterface mDeviceConfig;
@@ -133,6 +139,9 @@
case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
updateSystemGestureExclusionLogDebounceMillis();
break;
+ case KEY_RAW_SENSOR_LOGGING_ENABLED:
+ updateRawSensorDataLoggingEnabled();
+ break;
default:
break;
}
@@ -158,6 +167,12 @@
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
}
+ private void updateRawSensorDataLoggingEnabled() {
+ mRawSensorLoggingEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_RAW_SENSOR_LOGGING_ENABLED, false);
+ }
+
void dump(PrintWriter pw) {
pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
@@ -167,6 +182,8 @@
pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+ pw.print(" "); pw.print(KEY_RAW_SENSOR_LOGGING_ENABLED);
+ pw.print("="); pw.println(mRawSensorLoggingEnabled);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 27b7fab..09fbce0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -202,7 +202,7 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.sysprop.SurfaceFlingerProperties;
@@ -458,7 +458,8 @@
*/
static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f;
- final WindowManagerConstants mConstants;
+ @VisibleForTesting
+ WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
@@ -768,7 +769,7 @@
final EmbeddedWindowController mEmbeddedWindowController;
final AnrController mAnrController;
- private final ImpressionAttestationController mImpressionAttestationController;
+ private final ScreenshotHashController mScreenshotHashController;
private final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@@ -1418,7 +1419,7 @@
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
- mImpressionAttestationController = new ImpressionAttestationController(mContext);
+ mScreenshotHashController = new ScreenshotHashController(mContext);
setGlobalShadowSettings();
mAnrController = new AnrController(this);
mStartingSurfaceController = new StartingSurfaceController(this);
@@ -8660,38 +8661,38 @@
}
@Override
- public String[] getSupportedImpressionAlgorithms() {
- return mImpressionAttestationController.getSupportedImpressionAlgorithms();
+ public String[] getSupportedScreenshotHashingAlgorithms() {
+ return mScreenshotHashController.getSupportedHashingAlgorithms();
}
@Override
- public boolean verifyImpressionToken(ImpressionToken impressionToken) {
- return mImpressionAttestationController.verifyImpressionToken(impressionToken);
+ public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
+ return mScreenshotHashController.verifyScreenshotHash(screenshotHash);
}
- ImpressionToken generateImpressionToken(Session session, IWindow window,
+ ScreenshotHash generateScreenshotHash(Session session, IWindow window,
Rect boundsInWindow, String hashAlgorithm) {
final SurfaceControl displaySurfaceControl;
final Rect boundsInDisplay = new Rect(boundsInWindow);
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
- Slog.w(TAG, "Failed to generate impression token. Invalid window");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Invalid window");
return null;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
- Slog.w(TAG, "Failed to generate impression token. Window is not on a display");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display");
return null;
}
displaySurfaceControl = displayContent.getSurfaceControl();
- mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win,
+ mScreenshotHashController.calculateScreenshotHashBoundsLocked(win,
boundsInWindow, boundsInDisplay);
if (boundsInDisplay.isEmpty()) {
- Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen");
return null;
}
}
@@ -8711,11 +8712,11 @@
SurfaceControl.captureLayers(args);
if (screenshotHardwareBuffer == null
|| screenshotHardwareBuffer.getHardwareBuffer() == null) {
- Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot");
return null;
}
- return mImpressionAttestationController.generateImpressionToken(
+ return mScreenshotHashController.generateScreenshotHash(
screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
similarity index 96%
rename from services/core/java/com/android/server/policy/WindowOrientationListener.java
rename to services/core/java/com/android/server/wm/WindowOrientationListener.java
index 17e81da..da31bb2 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
@@ -31,6 +31,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.io.PrintWriter;
import java.util.List;
@@ -63,6 +65,7 @@
private OrientationJudge mOrientationJudge;
private int mCurrentRotation = -1;
private final Context mContext;
+ private final WindowManagerConstants mConstants;
private final Object mLock = new Object();
@@ -71,9 +74,11 @@
*
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
+ * @param wmService WindowManagerService to read the device config from.
*/
- public WindowOrientationListener(Context context, Handler handler) {
- this(context, handler, SensorManager.SENSOR_DELAY_UI);
+ public WindowOrientationListener(
+ Context context, Handler handler, WindowManagerService wmService) {
+ this(context, handler, wmService, SensorManager.SENSOR_DELAY_UI);
}
/**
@@ -81,6 +86,7 @@
*
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
+ * @param wmService WindowManagerService to read the device config from.
* @param rate at which sensor events are processed (see also
* {@link android.hardware.SensorManager SensorManager}). Use the default
* value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
@@ -88,10 +94,12 @@
*
* This constructor is private since no one uses it.
*/
- private WindowOrientationListener(Context context, Handler handler, int rate) {
+ private WindowOrientationListener(
+ Context context, Handler handler, WindowManagerService wmService, int rate) {
mContext = context;
mHandler = handler;
- mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+ mConstants = wmService.mConstants;
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
Sensor wakeUpDeviceOrientationSensor = null;
@@ -497,7 +505,7 @@
private static final float MIN_ACCELERATION_MAGNITUDE =
SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
private static final float MAX_ACCELERATION_MAGNITUDE =
- SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
+ SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
// Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
// when screen is facing the sky or ground), we completely ignore orientation data
@@ -1072,8 +1080,14 @@
mDesiredRotation = reportedRotation;
newRotation = evaluateRotationChangeLocked();
}
- if (newRotation >=0) {
+ if (newRotation >= 0) {
onProposedRotationChanged(newRotation);
+ if (mConstants.mRawSensorLoggingEnabled) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_ROTATED,
+ event.timestamp,
+ rotationToLogEnum(reportedRotation));
+ }
}
}
@@ -1180,5 +1194,20 @@
}
}
};
+
+ private int rotationToLogEnum(int rotation) {
+ switch (rotation) {
+ case 0:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_0;
+ case 1:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_90;
+ case 2:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_180;
+ case 3:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_270;
+ default:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__UNKNOWN;
+ }
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d235a7f..4b1bae4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16220,12 +16220,16 @@
final long identity = Binder.clearCallingIdentity();
try {
- int result = checkProvisioningPreConditionSkipPermission(
- ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
- if (result != CODE_OK) {
- throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
- "Provisioning preconditions failed with result: " + result);
+ // TODO(b/178187130): This check fails silent provisioning, uncomment once silent
+ // provisioning is no longer used.
+ if (false) {
+ int result = checkProvisioningPreConditionSkipPermission(
+ ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
+ if (result != CODE_OK) {
+ throw new ServiceSpecificException(
+ PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ "Provisioning preconditions failed with result: " + result);
+ }
}
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -16333,6 +16337,11 @@
private boolean setActiveAdminAndDeviceOwner(
@UserIdInt int userId, ComponentName adminComponent, String name) {
enableAndSetActiveAdmin(userId, userId, adminComponent);
- return setDeviceOwner(adminComponent, name, userId);
+ // TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
+ // longer used.
+ if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
+ return setDeviceOwner(adminComponent, name, userId);
+ }
+ return true;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e1aca55..2321a73 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -121,6 +121,8 @@
sMockWm = mock(WindowManagerService.class);
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
+ sMockWm.mConstants = mock(WindowManagerConstants.class);
+ sMockWm.mConstants.mRawSensorLoggingEnabled = true;
}
@AfterClass
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index eaa3e04..5cb0c17 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -80,7 +80,7 @@
}
namespace {
-void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+void CompileApkAssetsLayouts(const std::unique_ptr<android::ApkAssets>& assets,
CompilationTarget target, std::ostream& target_out) {
android::AssetManager2 resources;
resources.SetApkAssets({assets.get()});
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 8507d85..706e3cb 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -344,6 +344,7 @@
// TODO: Instead of doing this, we should create a formal way for cloning cell identity.
// Cell identity is not an immutable object so we have to deep copy it.
mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+ p.recycle();
}
if (nri.mVoiceSpecificInfo != null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e4386e75..65b8de2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14242,7 +14242,7 @@
* If this policy is enabled, data will be temporarily enabled on the non-default data SIM
* during any voice calls.
*
- * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
* @hide
*/
@SystemApi
@@ -14256,7 +14256,7 @@
* will also return true for {@link ApnSetting#TYPE_MMS}.
* When disabled, the MMS APN will be governed by the same rules as all other APNs.
*
- * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
* @hide
*/
@SystemApi
@@ -14284,11 +14284,11 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
+ public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
+ service.setMobileDataPolicyEnabled(getSubId(), policy, enabled);
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d17415a..0cd17da3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2172,7 +2172,7 @@
*/
String getMmsUAProfUrl(int subId);
- void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
+ void setMobileDataPolicyEnabled(int subscriptionId, int policy, boolean enabled);
boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);