Merge changes from topic "stream_combinations"
* changes:
Camera: Implement legacy device stream combination query
Camera: Add reprocessable mandatory stream combinations
Camera: Add support for mandatory stream combinations
diff --git a/Android.bp b/Android.bp
index 32bd408..e096c9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -535,6 +535,7 @@
"telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
"telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
"telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
+ "telephony/java/android/telephony/ims/aidl/IRcs.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
@@ -611,7 +612,6 @@
"telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
"telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
"telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
- "telephony/java/com/android/internal/telephony/rcs/IRcs.aidl",
"wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl",
"wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl",
"wifi/java/android/net/wifi/ISoftApCallback.aidl",
diff --git a/Android.mk b/Android.mk
index 92e33e9..e3cc275 100644
--- a/Android.mk
+++ b/Android.mk
@@ -72,6 +72,11 @@
$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
+# Run this for checkbuild
+checkbuild: doc-comment-check-docs
+# Check comment when you are updating the API
+update-api: doc-comment-check-docs
+
# ==== hiddenapi lists =======================================
.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
new file mode 100644
index 0000000..a482c4a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.textclassifier;
+
+import android.content.Context;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLanguage;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class TextClassifierPerfTest {
+ /** Request contains meaning text, rather than garbled text. */
+ private static final int ACTUAL_REQUEST = 0;
+ private static final String RANDOM_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameterized.Parameters(name = "size={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{{ACTUAL_REQUEST}, {10}, {100}, {1000}});
+ }
+
+ private TextClassifier mTextClassifier;
+ private final int mSize;
+
+ public TextClassifierPerfTest(int size) {
+ mSize = size;
+ }
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ TextClassificationManager textClassificationManager =
+ context.getSystemService(TextClassificationManager.class);
+ mTextClassifier = textClassificationManager.getTextClassifier();
+ }
+
+ @Test
+ public void testSuggestConversationActions() {
+ String text = mSize == ACTUAL_REQUEST ? "Where are you?" : generateRandomString(mSize);
+ ConversationActions.Request request = createConversationActionsRequest(text);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mTextClassifier.suggestConversationActions(request);
+ }
+ }
+
+ @Test
+ public void testDetectLanguage() {
+ String text = mSize == ACTUAL_REQUEST
+ ? "これは日本語のテキストです" : generateRandomString(mSize);
+ TextLanguage.Request request = createTextLanguageRequest(text);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mTextClassifier.detectLanguage(request);
+ }
+ }
+
+ private static ConversationActions.Request createConversationActionsRequest(CharSequence text) {
+ ConversationActions.Message message =
+ new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_REMOTE)
+ .setText(text)
+ .build();
+ return new ConversationActions.Request.Builder(Collections.singletonList(message))
+ .build();
+ }
+
+ private static TextLanguage.Request createTextLanguageRequest(CharSequence text) {
+ return new TextLanguage.Request.Builder(text).build();
+ }
+
+ private static String generateRandomString(int length) {
+ Random random = new Random();
+ StringBuilder stringBuilder = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ int index = random.nextInt(RANDOM_CHAR_SET.length());
+ stringBuilder.append(RANDOM_CHAR_SET.charAt(index));
+ }
+ return stringBuilder.toString();
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index 693248a..40f2d67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -57,6 +57,7 @@
field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
field public static final java.lang.String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
field public static final java.lang.String BROADCAST_WAP_PUSH = "android.permission.BROADCAST_WAP_PUSH";
+ field public static final java.lang.String CALL_COMPANION_APP = "android.permission.CALL_COMPANION_APP";
field public static final java.lang.String CALL_PHONE = "android.permission.CALL_PHONE";
field public static final java.lang.String CALL_PRIVILEGED = "android.permission.CALL_PRIVILEGED";
field public static final java.lang.String CAMERA = "android.permission.CAMERA";
@@ -91,7 +92,6 @@
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
- field public static final java.lang.String CALL_COMPANION_APP = "android.permission.CALL_COMPANION_APP";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -11227,6 +11227,15 @@
field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400
}
+ public final class ModuleInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getName();
+ method public java.lang.String getPackageName();
+ method public boolean isHidden();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.ModuleInfo> CREATOR;
+ }
+
public class PackageInfo implements android.os.Parcelable {
ctor public PackageInfo();
method public int describeContents();
@@ -11443,6 +11452,7 @@
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+ method public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
method public abstract byte[] getInstantAppCookie();
@@ -11450,6 +11460,7 @@
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
+ method public android.content.pm.ModuleInfo getModuleInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String getNameForUid(int);
method public android.content.pm.PackageInfo getPackageArchiveInfo(java.lang.String, int);
method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -13561,6 +13572,46 @@
ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
}
+ public final class BlendMode extends java.lang.Enum {
+ method public static android.graphics.BlendMode valueOf(java.lang.String);
+ method public static final android.graphics.BlendMode[] values();
+ enum_constant public static final android.graphics.BlendMode CLEAR;
+ enum_constant public static final android.graphics.BlendMode COLOR;
+ enum_constant public static final android.graphics.BlendMode COLOR_BURN;
+ enum_constant public static final android.graphics.BlendMode COLOR_DODGE;
+ enum_constant public static final android.graphics.BlendMode DARKEN;
+ enum_constant public static final android.graphics.BlendMode DIFFERENCE;
+ enum_constant public static final android.graphics.BlendMode DST;
+ enum_constant public static final android.graphics.BlendMode DST_ATOP;
+ enum_constant public static final android.graphics.BlendMode DST_IN;
+ enum_constant public static final android.graphics.BlendMode DST_OUT;
+ enum_constant public static final android.graphics.BlendMode DST_OVER;
+ enum_constant public static final android.graphics.BlendMode EXCLUSION;
+ enum_constant public static final android.graphics.BlendMode HARD_LIGHT;
+ enum_constant public static final android.graphics.BlendMode HUE;
+ enum_constant public static final android.graphics.BlendMode LIGHTEN;
+ enum_constant public static final android.graphics.BlendMode LUMINOSITY;
+ enum_constant public static final android.graphics.BlendMode MODULATE;
+ enum_constant public static final android.graphics.BlendMode MULTIPLY;
+ enum_constant public static final android.graphics.BlendMode OVERLAY;
+ enum_constant public static final android.graphics.BlendMode PLUS;
+ enum_constant public static final android.graphics.BlendMode SATURATION;
+ enum_constant public static final android.graphics.BlendMode SCREEN;
+ enum_constant public static final android.graphics.BlendMode SOFT_LIGHT;
+ enum_constant public static final android.graphics.BlendMode SRC;
+ enum_constant public static final android.graphics.BlendMode SRC_ATOP;
+ enum_constant public static final android.graphics.BlendMode SRC_IN;
+ enum_constant public static final android.graphics.BlendMode SRC_OUT;
+ enum_constant public static final android.graphics.BlendMode SRC_OVER;
+ enum_constant public static final android.graphics.BlendMode XOR;
+ }
+
+ public final class BlendModeColorFilter extends android.graphics.ColorFilter {
+ ctor public BlendModeColorFilter(int, android.graphics.BlendMode);
+ method public int getColor();
+ method public android.graphics.BlendMode getMode();
+ }
+
public class BlurMaskFilter extends android.graphics.MaskFilter {
ctor public BlurMaskFilter(float, android.graphics.BlurMaskFilter.Blur);
}
@@ -13623,7 +13674,8 @@
method public void drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint);
method public void drawCircle(float, float, float, android.graphics.Paint);
method public void drawColor(int);
- method public void drawColor(int, android.graphics.PorterDuff.Mode);
+ method public deprecated void drawColor(int, android.graphics.PorterDuff.Mode);
+ method public void drawColor(int, android.graphics.BlendMode);
method public void drawDoubleRoundRect(android.graphics.RectF, float, float, android.graphics.RectF, float, float, android.graphics.Paint);
method public void drawDoubleRoundRect(android.graphics.RectF, float[], android.graphics.RectF, float[], android.graphics.Paint);
method public void drawLine(float, float, float, float, android.graphics.Paint);
@@ -14245,6 +14297,7 @@
method public float descent();
method public boolean equalsForTextMeasurement(android.graphics.Paint);
method public int getAlpha();
+ method public android.graphics.BlendMode getBlendMode();
method public int getColor();
method public android.graphics.ColorFilter getColorFilter();
method public boolean getFillPath(android.graphics.Path, android.graphics.Path);
@@ -14299,7 +14352,7 @@
method public float getUnderlinePosition();
method public float getUnderlineThickness();
method public float getWordSpacing();
- method public android.graphics.Xfermode getXfermode();
+ method public deprecated android.graphics.Xfermode getXfermode();
method public boolean hasGlyph(java.lang.String);
method public final boolean isAntiAlias();
method public final boolean isDither();
@@ -14319,6 +14372,7 @@
method public void setARGB(int, int, int, int);
method public void setAlpha(int);
method public void setAntiAlias(boolean);
+ method public void setBlendMode(android.graphics.BlendMode);
method public void setColor(int);
method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
method public void setDither(boolean);
@@ -14352,7 +14406,7 @@
method public android.graphics.Typeface setTypeface(android.graphics.Typeface);
method public void setUnderlineText(boolean);
method public void setWordSpacing(float);
- method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
+ method public deprecated android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
field public static final int CURSOR_AFTER = 0; // 0x0
field public static final int CURSOR_AT = 4; // 0x4
@@ -14634,7 +14688,7 @@
enum_constant public static final android.graphics.PorterDuff.Mode XOR;
}
- public class PorterDuffColorFilter extends android.graphics.ColorFilter {
+ public deprecated class PorterDuffColorFilter extends android.graphics.ColorFilter {
ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
}
@@ -15190,7 +15244,7 @@
method public final void setCallback(android.graphics.drawable.Drawable.Callback);
method public void setChangingConfigurations(int);
method public abstract void setColorFilter(android.graphics.ColorFilter);
- method public void setColorFilter(int, android.graphics.PorterDuff.Mode);
+ method public deprecated void setColorFilter(int, android.graphics.PorterDuff.Mode);
method public deprecated void setDither(boolean);
method public void setFilterBitmap(boolean);
method public void setHotspot(float, float);
@@ -26279,9 +26333,12 @@
field public static final int SUCCESS = 0; // 0x0
}
- public static class AudioEffect.Descriptor {
+ public static final class AudioEffect.Descriptor implements android.os.Parcelable {
ctor public AudioEffect.Descriptor();
ctor public AudioEffect.Descriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.audiofx.AudioEffect.Descriptor> CREATOR;
field public java.lang.String connectMode;
field public java.lang.String implementor;
field public java.lang.String name;
@@ -37492,25 +37549,37 @@
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
method public static android.net.Uri copyDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method public static android.net.Uri createDocument(android.content.ContentInterface, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static deprecated android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public static android.content.IntentSender createWebLinkIntent(android.content.ContentInterface, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public static deprecated android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
method public static boolean deleteDocument(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static void ejectRoot(android.content.ContentInterface, android.net.Uri);
+ method public static deprecated void ejectRoot(android.content.ContentResolver, android.net.Uri);
method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
method public static android.os.Bundle getDocumentMetadata(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+ method public static deprecated android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isChildDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
method public static boolean isRootUri(android.content.Context, android.net.Uri);
method public static boolean isRootsUri(android.content.Context, android.net.Uri);
method public static boolean isTreeUri(android.net.Uri);
method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method public static boolean removeDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static deprecated boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method public static android.net.Uri renameDocument(android.content.ContentInterface, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public static deprecated android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -43181,6 +43250,7 @@
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
field public static final java.lang.String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
+ field public static final java.lang.String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -43188,14 +43258,13 @@
field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
- field public static final java.lang.String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
+ field public static final java.lang.String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
- field public static final java.lang.String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -48906,7 +48975,9 @@
method public float getPressure();
method public float getPressure(int);
method public float getRawX();
+ method public float getRawX(int);
method public float getRawY();
+ method public float getRawY(int);
method public float getSize();
method public float getSize(int);
method public int getSource();
diff --git a/api/removed.txt b/api/removed.txt
index e3e8b63..f7106d2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -508,21 +508,6 @@
field public static final deprecated java.lang.String TIMESTAMP = "timestamp";
}
- public final class DocumentsContract {
- method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
- method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
- method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
- method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
- method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
- method public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
- method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
- }
-
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index da6840c..5a49f0bf3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6559,6 +6559,7 @@
field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
+ field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
@@ -6582,26 +6583,39 @@
field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
+ field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
+ field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
field public static final int CODE_SIP_BUSY = 338; // 0x152
+ field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
+ field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
+ field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
+ field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
+ field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
+ field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
+ field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
+ field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
+ field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
+ field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
+ field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
@@ -6611,9 +6625,11 @@
field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
field public static final int CODE_UNSPECIFIED = 0; // 0x0
+ field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
field public static final int CODE_USER_DECLINE = 504; // 0x1f8
field public static final int CODE_USER_IGNORE = 503; // 0x1f7
field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
+ field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f58caff..410bd19 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2394,16 +2394,23 @@
}
/**
- * Pulls low power state information. This includes platform and subsystem sleep state information,
- * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState as defined in
+ * Pulls low power state information. If power.stats HAL is not available, this
+ * includes platform and subsystem sleep state information,
+ * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState
+ * as defined in:
* hardware/interfaces/power/1.0/types.hal
* hardware/interfaces/power/1.1/types.hal
+ * If power.stats HAL is available, this includes PowerEntityStateResidencyResult
+ * as defined in:
+ * hardware/interfaces/power/stats/1.0/types.hal
*/
message SubsystemSleepState {
// Subsystem name
optional string subsystem_name = 1;
// For PlatformLowPowerStats (hal 1.0), this is the voter name, which could be empty.
// For SubsystemLowPowerStats (hal 1.1), this is the sleep state name.
+ // For PowerEntityStateResidencyResult (hal power/stats/1.0) this is the
+ // powerEntityStateName from the corresponding PowerEntityStateInfo.
optional string subname = 2;
// The number of times it entered, or voted for entering the sleep state
optional uint64 count = 3;
@@ -2540,15 +2547,19 @@
/*
* Logs the memory stats for a process.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService).
*/
message ProcessMemoryState {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
// The process name.
+ // Usually package name, "system" for system server.
+ // Provided by ActivityManagerService.
optional string process_name = 2;
- // oom adj score.
+ // Current OOM score adjustment. Value read from ProcessRecord.
optional int32 oom_adj_score = 3;
// # of page-faults
@@ -2558,12 +2569,18 @@
optional int64 page_major_fault = 5;
// RSS
+ // Value is read from /proc/PID/stat, field 24. Or from memory.stat, field
+ // total_rss if per-app memory cgroups are enabled.
optional int64 rss_in_bytes = 6;
// CACHE
+ // Value is read from memory.stat, field total_cache if per-app memory
+ // cgroups are enabled. Otherwise, 0.
optional int64 cache_in_bytes = 7;
// SWAP
+ // Value is read from memory.stat, field total_swap if per-app memory
+ // cgroups are enabled. Otherwise, 0.
optional int64 swap_in_bytes = 8;
// Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
@@ -2576,12 +2593,15 @@
/*
* Logs the memory stats for a native process (from procfs).
+ *
+ * Pulled from StatsCompanionService for selected native processes.
*/
message NativeProcessMemoryState {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
// The process name.
+ // Value read from /proc/PID/cmdline.
optional string process_name = 2;
// # of page-faults
@@ -2591,6 +2611,7 @@
optional int64 page_major_fault = 4;
// RSS
+ // Value read from /proc/PID/stat, field 24.
optional int64 rss_in_bytes = 5;
// Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
@@ -2603,13 +2624,17 @@
/*
* Logs the memory high-water mark for a process.
- * Recorded in ActivityManagerService.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
+ * and for selected native processes.
*/
message ProcessMemoryHighWaterMark {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
- // The process name. Provided by ActivityManagerService or read from /proc/PID/cmdline.
+ // The process name.
+ // Usually package name or process cmdline.
+ // Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
// RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
@@ -3586,4 +3611,4 @@
*/
message DocsUIInvalidScopedAccessRequestReported {
optional android.stats.docsui.InvalidScopedAccess type = 1;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index 4501b64..c8c3920 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -19,6 +19,8 @@
#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+
#include <fcntl.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
@@ -42,9 +44,12 @@
using android::hardware::power::V1_0::IPower;
using android::hardware::power::V1_0::PowerStatePlatformSleepState;
using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_0::Status;
using android::hardware::power::V1_1::PowerStateSubsystem;
using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
+using android::hardware::power::stats::V1_0::PowerEntityInfo;
+using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+
using android::hardware::Return;
using android::hardware::Void;
@@ -55,44 +60,209 @@
namespace os {
namespace statsd {
+std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
+
sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
+sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+
+std::unordered_map<uint32_t, std::string> gEntityNames = {};
+std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
+
std::mutex gPowerHalMutex;
-bool gPowerHalExists = true;
-bool getPowerHal() {
- if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
- gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
- if (gPowerHalV1_0 != nullptr) {
- gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
- ALOGI("Loaded power HAL service");
- } else {
- ALOGW("Couldn't load power HAL service");
- gPowerHalExists = false;
- }
+// The caller must be holding gPowerHalMutex.
+void deinitPowerStatsLocked() {
+ gPowerHalV1_0 = nullptr;
+ gPowerHalV1_1 = nullptr;
+ gPowerStatsHalV1_0 = nullptr;
+}
+
+struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override {
+ // The HAL just died. Reset all handles to HAL services.
+ std::lock_guard<std::mutex> lock(gPowerHalMutex);
+ deinitPowerStatsLocked();
}
- return gPowerHalV1_0 != nullptr;
+};
+
+sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
+
+SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
+ StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
}
-SubsystemSleepStatePuller::SubsystemSleepStatePuller() : StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
+// The caller must be holding gPowerHalMutex.
+bool checkResultLocked(const Return<void> &ret, const char* function) {
+ if (!ret.isOk()) {
+ ALOGE("%s failed: requested HAL service not available. Description: %s",
+ function, ret.description().c_str());
+ if (ret.isDeadObject()) {
+ deinitPowerStatsLocked();
+ }
+ return false;
+ }
+ return true;
}
-bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
+// The caller must be holding gPowerHalMutex.
+// gPowerStatsHalV1_0 must not be null
+bool initializePowerStats() {
+ using android::hardware::power::stats::V1_0::Status;
- if (!getPowerHal()) {
- ALOGE("Power Hal not loaded");
+ // Clear out previous content if we are re-initializing
+ gEntityNames.clear();
+ gStateNames.clear();
+
+ Return<void> ret;
+ ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting power entity info");
+ return;
+ }
+
+ // construct lookup table of powerEntityId to power entity name
+ for (auto info : infos) {
+ gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ return false;
+ }
+
+ ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting state info");
+ return;
+ }
+
+ // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
+ for (auto stateSpace : stateSpaces) {
+ std::unordered_map<uint32_t, std::string> stateNames = {};
+ for (auto state : stateSpace.states) {
+ stateNames.emplace(state.powerEntityStateId,
+ state.powerEntityStateName);
+ }
+ gStateNames.emplace(stateSpace.powerEntityId, stateNames);
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ return false;
+ }
+
+ return (!gEntityNames.empty()) && (!gStateNames.empty());
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getPowerStatsHalLocked() {
+ if(gPowerStatsHalV1_0 == nullptr) {
+ gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
+ if (gPowerStatsHalV1_0 == nullptr) {
+ ALOGE("Unable to get power.stats HAL service.");
+ return false;
+ }
+
+ // Link death recipient to power.stats service handle
+ hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power.stats HAL death: %s",
+ linked.description().c_str());
+ deinitPowerStatsLocked();
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power.stats HAL death notifications");
+ // We should still continue even though linking failed
+ }
+ return initializePowerStats();
+ }
+ return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
+ using android::hardware::power::stats::V1_0::Status;
+
+ if(!getPowerStatsHalLocked()) {
return false;
}
int64_t wallClockTimestampNs = getWallClockNs();
int64_t elapsedTimestampNs = getElapsedRealtimeNs();
- data->clear();
+ // Get power entity state residency data
+ bool success = false;
+ Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
+ [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
+ (auto results, auto status) {
+ if (status == Status::NOT_SUPPORTED) {
+ ALOGW("getPowerEntityStateResidencyData is not supported");
+ success = false;
+ return;
+ }
- Return<void> ret;
+ for(auto result : results) {
+ for(auto stateResidency : result.stateResidencyData) {
+ auto statePtr = make_shared<LogEvent>(
+ android::util::SUBSYSTEM_SLEEP_STATE,
+ wallClockTimestampNs, elapsedTimestampNs);
+ statePtr->write(gEntityNames.at(result.powerEntityId));
+ statePtr->write(gStateNames.at(result.powerEntityId)
+ .at(stateResidency.powerEntityStateId));
+ statePtr->write(stateResidency.totalStateEntryCount);
+ statePtr->write(stateResidency.totalTimeInStateMs);
+ statePtr->init();
+ data->emplace_back(statePtr);
+ }
+ }
+ success = true;
+ });
+ // Intentionally not returning early here.
+ // bool success determines if this succeeded or not.
+ checkResultLocked(ret, __func__);
+
+ return success;
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getPowerHalLocked() {
+ if(gPowerHalV1_0 == nullptr) {
+ gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
+ if(gPowerHalV1_0 == nullptr) {
+ ALOGE("Unable to get power HAL service.");
+ return false;
+ }
+ gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
+
+ // Link death recipient to power service handle
+ hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power HAL death: %s",
+ linked.description().c_str());
+ gPowerHalV1_0 = nullptr;
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power. death notifications");
+ // We should still continue even though linking failed
+ }
+ }
+ return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
+ using android::hardware::power::V1_0::Status;
+
+ if(!getPowerHalLocked()) {
+ return false;
+ }
+
+ int64_t wallClockTimestampNs = getWallClockNs();
+ int64_t elapsedTimestampNs = getElapsedRealtimeNs();
+ Return<void> ret;
ret = gPowerHalV1_0->getPlatformLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
+ [&data, wallClockTimestampNs, elapsedTimestampNs]
+ (hidl_vec<PowerStatePlatformSleepState> states, Status status) {
if (status != Status::SUCCESS) return;
for (size_t i = 0; i < states.size(); i++) {
@@ -128,9 +298,7 @@
}
}
});
- if (!ret.isOk()) {
- ALOGE("getLowPowerStats() failed: power HAL service not available");
- gPowerHalV1_0 = nullptr;
+ if (!checkResultLocked(ret, __func__)) {
return false;
}
@@ -139,35 +307,68 @@
android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
if (gPowerHal_1_1 != nullptr) {
ret = gPowerHal_1_1->getSubsystemLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
- if (status != Status::SUCCESS) return;
+ [&data, wallClockTimestampNs, elapsedTimestampNs]
+ (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
+ if (status != Status::SUCCESS) return;
- if (subsystems.size() > 0) {
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem& subsystem = subsystems[i];
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state =
- subsystem.states[j];
- auto subsystemStatePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- subsystemStatePtr->write(subsystem.name);
- subsystemStatePtr->write(state.name);
- subsystemStatePtr->write(state.totalTransitions);
- subsystemStatePtr->write(state.residencyInMsecSinceBoot);
- subsystemStatePtr->init();
- data->push_back(subsystemStatePtr);
- VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
- subsystem.name.c_str(), state.name.c_str(),
- (long long)state.residencyInMsecSinceBoot,
- (long long)state.totalTransitions,
- (long long)state.lastEntryTimestampMs);
- }
- }
+ if (subsystems.size() > 0) {
+ for (size_t i = 0; i < subsystems.size(); i++) {
+ const PowerStateSubsystem& subsystem = subsystems[i];
+ for (size_t j = 0; j < subsystem.states.size(); j++) {
+ const PowerStateSubsystemSleepState& state =
+ subsystem.states[j];
+ auto subsystemStatePtr = make_shared<LogEvent>(
+ android::util::SUBSYSTEM_SLEEP_STATE,
+ wallClockTimestampNs, elapsedTimestampNs);
+ subsystemStatePtr->write(subsystem.name);
+ subsystemStatePtr->write(state.name);
+ subsystemStatePtr->write(state.totalTransitions);
+ subsystemStatePtr->write(state.residencyInMsecSinceBoot);
+ subsystemStatePtr->init();
+ data->push_back(subsystemStatePtr);
+ VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
+ subsystem.name.c_str(), state.name.c_str(),
+ (long long)state.residencyInMsecSinceBoot,
+ (long long)state.totalTransitions,
+ (long long)state.lastEntryTimestampMs);
}
- });
+ }
+ }
+ });
}
- return true;
+ return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
+ std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};
+
+ // First see if power.stats HAL is available. Fall back to power HAL if
+ // power.stats HAL is unavailable.
+ if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
+ ALOGI("Using power.stats HAL");
+ ret = getIPowerStatsDataLocked;
+ } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
+ ALOGI("Using power HAL");
+ ret = getIPowerDataLocked;
+ }
+
+ return ret;
+}
+
+bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+ if(!gPuller) {
+ gPuller = getPullerLocked();
+ }
+
+ if(gPuller) {
+ return gPuller(data);
+ }
+
+ ALOGE("Unable to load Power Hal or power.stats HAL");
+ return false;
}
} // namespace statsd
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 597377e..8464b8df 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -70,9 +70,16 @@
/**
* Dumps the report from the device and converts it to a ConfigMetricsReportList.
* Erases the data if clearData is true.
+ * @param configId id of the config
+ * @param clearData whether to erase the report data from statsd after getting the report.
+ * @param useShellUid Pulls data for the {@link SHELL_UID} instead of the caller's uid.
+ * @param logger Logger to log error messages
+ * @return
+ * @throws IOException
+ * @throws InterruptedException
*/
public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
- Logger logger) throws IOException, InterruptedException {
+ boolean useShellUid, Logger logger) throws IOException, InterruptedException {
try {
File outputFile = File.createTempFile("statsdret", ".bin");
outputFile.deleteOnExit();
@@ -82,7 +89,7 @@
"adb",
"shell",
CMD_DUMP_REPORT,
- SHELL_UID,
+ useShellUid ? SHELL_UID : "",
String.valueOf(configId),
clearData ? "" : "--keep_data",
"--include_current_bucket",
@@ -93,8 +100,8 @@
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
logger.severe("Failed to fetch and parse the statsd output report. "
+ "Perhaps there is not a valid statsd config for the requested "
- + "uid=" + SHELL_UID
- + ", configId=" + configId
+ + (useShellUid ? ("uid=" + SHELL_UID + ", ") : "")
+ + "configId=" + configId
+ ".");
throw (e);
}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 08074ed..67fb906 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -165,7 +165,7 @@
try {
Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
Utils.SHELL_UID, String.valueOf(configId));
- Utils.getReportList(configId, true /* clearData */, sLogger);
+ Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
} catch (InterruptedException | IOException e) {
sLogger.severe("Failed to remove config: " + e.getMessage());
return false;
@@ -234,7 +234,7 @@
// Even if the args request no modifications, we still parse it to make sure it's valid.
ConfigMetricsReportList reportList;
try {
- reportList = Utils.getReportList(configId, clearData, sLogger);
+ reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger);
} catch (IOException | InterruptedException e) {
sLogger.severe("Failed to get report list: " + e.getMessage());
return false;
@@ -278,7 +278,7 @@
sLogger.fine(String.format("cmdClear with %d", configId));
try {
- Utils.getReportList(configId, true /* clearData */, sLogger);
+ Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
} catch (IOException | InterruptedException e) {
sLogger.severe("Failed to get report list: " + e.getMessage());
return false;
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index f7bd44a..e3fe928 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -165,7 +165,7 @@
}
private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, logger);
+ ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, logger);
// We may get multiple reports. Take the last one.
ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
// Really should be only one metric.
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index bacb991..bf14438 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2730,8 +2730,6 @@
Lcom/android/internal/telephony/CommandsInterface;->changeBarringPassword(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
Lcom/android/internal/telephony/CommandsInterface;->deleteSmsOnRuim(ILandroid/os/Message;)V
Lcom/android/internal/telephony/CommandsInterface;->deleteSmsOnSim(ILandroid/os/Message;)V
-Lcom/android/internal/telephony/CommandsInterface;->dial(Ljava/lang/String;ILandroid/os/Message;)V
-Lcom/android/internal/telephony/CommandsInterface;->dial(Ljava/lang/String;ILcom/android/internal/telephony/UUSInfo;Landroid/os/Message;)V
Lcom/android/internal/telephony/CommandsInterface;->exitEmergencyCallbackMode(Landroid/os/Message;)V
Lcom/android/internal/telephony/CommandsInterface;->getBasebandVersion(Landroid/os/Message;)V
Lcom/android/internal/telephony/CommandsInterface;->getCdmaBroadcastConfig(Landroid/os/Message;)V
@@ -2861,7 +2859,6 @@
Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
@@ -2872,10 +2869,8 @@
Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DcFailCause;)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfDisconnectDcRetrying(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyConnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;Lcom/android/internal/telephony/dataconnection/DcFailCause;Z)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
@@ -2884,22 +2879,6 @@
Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ACTIVATION_REJECT_GGSN:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ACTIVATION_REJECT_UNSPECIFIED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->APN_TYPE_CONFLICT:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->INSUFFICIENT_RESOURCES:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->MISSING_UNKNOWN_APN:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->NSAPI_IN_USE:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_IPV4_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_IPV6_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_SINGLE_BEARER_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->OPERATOR_BARRED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->PROTOCOL_ERRORS:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_NOT_SUBSCRIBED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_NOT_SUPPORTED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_OUT_OF_ORDER:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->UNKNOWN_PDP_ADDRESS_TYPE:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->USER_AUTHENTICATION:Lcom/android/internal/telephony/dataconnection/DcFailCause;
Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
@@ -3763,8 +3742,6 @@
Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String;
Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V
Lcom/android/internal/telephony/test/SimulatedCommands;->acceptCall(Landroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->dial(Ljava/lang/String;ILandroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->dial(Ljava/lang/String;ILcom/android/internal/telephony/UUSInfo;Landroid/os/Message;)V
Lcom/android/internal/telephony/test/SimulatedCommands;->mDcSuccess:Z
Lcom/android/internal/telephony/test/SimulatedCommands;->resultFail(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)V
Lcom/android/internal/telephony/test/SimulatedCommands;->resultSuccess(Landroid/os/Message;Ljava/lang/Object;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5e445d14..b584d5d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2545,7 +2545,7 @@
* picture-in-picture.
*
* @return true if the system successfully put this activity into picture-in-picture mode or was
- * already in picture-in-picture mode (@see {@link #isInPictureInPictureMode()). If the device
+ * already in picture-in-picture mode (see {@link #isInPictureInPictureMode()}). If the device
* does not support picture-in-picture, return false.
*/
public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f928501..b42d53a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -306,6 +306,6 @@
public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
ProfilerInfo profilerInfo, Object wmLock);
- /** Checks if process running with given pid has access to full external storage or not */
- public abstract boolean isAppStorageSandboxed(int pid, int uid);
+ /** Returns mount mode for process running with given pid */
+ public abstract int getStorageMountMode(int pid, int uid);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 67d9ad6e..2b81c86 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -44,6 +44,7 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
@@ -791,6 +792,16 @@
throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
}
+ @Override
+ public List<ModuleInfo> getInstalledModules(int flags) {
+ return null;
+ }
+
+ @Override
+ public ModuleInfo getModuleInfo(String packageName, int flags) throws NameNotFoundException {
+ return null;
+ }
+
@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index ebbf317..392921e 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -173,7 +173,7 @@
/**
* Returns true if the input only accepts data, meaning {@link #getAllowFreeFormInput}
- * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes is
+ * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes} is
* non-null and not empty.
*/
public boolean isDataOnly() {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 43e1836..f4fd5d1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -154,7 +154,7 @@
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccManager;
-import android.telephony.rcs.RcsManager;
+import android.telephony.ims.RcsManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.ContextThemeWrapper;
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 6248e7c..5b87de4 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -5,7 +5,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -222,7 +221,6 @@
* @param componentName ComponentName of a VR InputMethod that should be set as selected
* input by InputMethodManagerService.
*/
- @TestApi
@RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
public void setVrInputMethod(ComponentName componentName) {
try {
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index 5baf2e2..ad9f680 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -16,11 +16,14 @@
package android.app;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Information returned after waiting for an activity start.
@@ -28,11 +31,43 @@
* @hide
*/
public class WaitResult implements Parcelable {
+
+ /**
+ * The state at which a launch sequence had started.
+ *
+ * @see <a href="https://developer.android.com/topic/performance/vitals/launch-time"App startup
+ * time</a>
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"LAUNCH_STATE_"}, value = {
+ LAUNCH_STATE_COLD,
+ LAUNCH_STATE_WARM,
+ LAUNCH_STATE_HOT
+ })
+ public @interface LaunchState {
+ }
+
+ /**
+ * Cold launch sequence: a new process has started.
+ */
+ public static final int LAUNCH_STATE_COLD = 1;
+
+ /**
+ * Warm launch sequence: process reused, but activity has to be created.
+ */
+ public static final int LAUNCH_STATE_WARM = 2;
+
+ /**
+ * Hot launch sequence: process reused, activity brought-to-top.
+ */
+ public static final int LAUNCH_STATE_HOT = 3;
+
public static final int INVALID_DELAY = -1;
public int result;
public boolean timeout;
public ComponentName who;
public long totalTime;
+ public @LaunchState int launchState;
public WaitResult() {
}
@@ -48,6 +83,7 @@
dest.writeInt(timeout ? 1 : 0);
ComponentName.writeToParcel(who, dest);
dest.writeLong(totalTime);
+ dest.writeInt(launchState);
}
public static final Parcelable.Creator<WaitResult> CREATOR
@@ -68,6 +104,7 @@
timeout = source.readInt() != 0;
who = ComponentName.readFromParcel(source);
totalTime = source.readLong();
+ launchState = source.readInt();
}
public void dump(PrintWriter pw, String prefix) {
@@ -76,5 +113,19 @@
pw.println(prefix + " timeout=" + timeout);
pw.println(prefix + " who=" + who);
pw.println(prefix + " totalTime=" + totalTime);
+ pw.println(prefix + " launchState=" + launchState);
+ }
+
+ public static String launchStateToString(@LaunchState int type) {
+ switch (type) {
+ case LAUNCH_STATE_COLD:
+ return "COLD";
+ case LAUNCH_STATE_WARM:
+ return "WARM";
+ case LAUNCH_STATE_HOT:
+ return "HOT";
+ default:
+ return "UNKNOWN (" + type + ")";
+ }
}
}
\ No newline at end of file
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index ca60e14..0ccd49f 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -209,7 +209,7 @@
*
* @param sliceUri Uri to bind.
* @param supportedSpecs List of supported specs.
- * @see Slice.
+ * @see Slice
* @see Slice#HINT_PARTIAL
*/
public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8e6a385..87b64797 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -643,6 +643,7 @@
private final IBluetoothManager mManagerService;
@UnsupportedAppUsage
private IBluetooth mService;
+ private Context mContext;
private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
private final Object mLock = new Object();
@@ -1541,6 +1542,23 @@
}
/**
+ * Set the context for this BluetoothAdapter (only called from BluetoothManager)
+ * @hide
+ */
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ private String getOpPackageName() {
+ // Workaround for legacy API for getting a BluetoothAdapter not
+ // passing a context
+ if (mContext != null) {
+ return mContext.getOpPackageName();
+ }
+ return ActivityThread.currentOpPackageName();
+ }
+
+ /**
* Start the remote device discovery process.
* <p>The discovery process usually involves an inquiry scan of about 12
* seconds, followed by a page scan of each new device to retrieve its
@@ -1577,7 +1595,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.startDiscovery();
+ return mService.startDiscovery(getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e3672a7..e08d405 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,6 +67,7 @@
}
// Legacy api - getDefaultAdapter does not take in the context
mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAdapter.setContext(context);
}
/**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5a12e4e..f138d39 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -327,6 +327,7 @@
public ContentProviderResult[] applyBatch(String callingPkg, String authority,
ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
+ validateIncomingAuthority(authority);
int numOperations = operations.size();
final int[] userIds = new int[numOperations];
for (int i = 0; i < numOperations; i++) {
@@ -447,6 +448,7 @@
@Override
public Bundle call(String callingPkg, String authority, String method, @Nullable String arg,
@Nullable Bundle extras) {
+ validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
final String original = setCallingPackage(callingPkg);
@@ -1182,12 +1184,12 @@
* Implement this to handle query requests where the arguments are packed into a {@link Bundle}.
* Arguments may include traditional SQL style query arguments. When present these
* should be handled according to the contract established in
- * {@link #query(Uri, String[], String, String[], String, CancellationSignal).
+ * {@link #query(Uri, String[], String, String[], String, CancellationSignal)}.
*
* <p>Traditional SQL arguments can be found in the bundle using the following keys:
- * <li>{@link ContentResolver#QUERY_ARG_SQL_SELECTION}
- * <li>{@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}
- * <li>{@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER}
+ * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SELECTION}
+ * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}
+ * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SORT_ORDER}
*
* <p>This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
@@ -1244,8 +1246,8 @@
return cursor;</pre>
* <p>
- * @see #query(Uri, String[], String, String[], String, CancellationSignal) for
- * implementation details.
+ * See {@link #query(Uri, String[], String, String[], String, CancellationSignal)}
+ * for implementation details.
*
* @param uri The URI to query. This will be the full URI sent by the client.
* @param projection The list of columns to put into the cursor.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d7d3cb5..b39010d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4462,7 +4462,7 @@
/**
* Use with {@link #getSystemService(String)} to retrieve an
- * {@link android.telephony.rcs.RcsManager}.
+ * {@link android.telephony.ims.RcsManager}.
* @hide
*/
public static final String TELEPHONY_RCS_SERVICE = "ircs";
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
new file mode 100644
index 0000000..07e640b
--- /dev/null
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information you can retrieve about a particular system
+ * module.
+ */
+public final class ModuleInfo implements Parcelable {
+
+ // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+ // constructor, and writeToParcel.
+
+ /** Public name of this module. */
+ private String mName;
+
+ /** The package name of this module. */
+ private String mPackageName;
+
+ /** Whether or not this module is hidden from the user. */
+ private boolean mHidden;
+
+ // TODO: Decide whether we need an additional metadata bundle to support out of band
+ // updates to ModuleInfo.
+ //
+ // private Bundle mMetadata;
+
+ /** @hide */
+ public ModuleInfo() {
+ }
+
+ /** @hide */
+ public ModuleInfo(ModuleInfo orig) {
+ mName = orig.mName;
+ mPackageName = orig.mPackageName;
+ mHidden = orig.mHidden;
+ }
+
+ /** @hide Sets the public name of this module. */
+ public ModuleInfo setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ /** Gets the public name of this module. */
+ public @Nullable String getName() {
+ return mName;
+ }
+
+ /** @hide Sets the package name of this module. */
+ public ModuleInfo setPackageName(String packageName) {
+ mPackageName = packageName;
+ return this;
+ }
+
+ /** Gets the package name of this module. */
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /** @hide Sets whether or not this package is hidden. */
+ public ModuleInfo setHidden(boolean hidden) {
+ mHidden = hidden;
+ return this;
+ }
+
+ /** Gets whether or not this package is hidden. */
+ public boolean isHidden() {
+ return mHidden;
+ }
+
+ /** Returns a string representation of this object. */
+ public String toString() {
+ return "ModuleInfo{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + mName + "}";
+ }
+
+ /** Describes the kinds of special objects contained in this object. */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+ hashCode = 31 * hashCode + Objects.hashCode(mName);
+ hashCode = 31 * hashCode + Objects.hashCode(mPackageName);
+ hashCode = 31 * hashCode + Boolean.hashCode(mHidden);
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ModuleInfo)) {
+ return false;
+ }
+ final ModuleInfo other = (ModuleInfo) obj;
+ return Objects.equals(mName, other.mName)
+ && Objects.equals(mPackageName, other.mPackageName)
+ && mHidden == other.mHidden;
+ }
+
+ /** Flattens this object into the given {@link Parcel}. */
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(mName);
+ dest.writeString(mPackageName);
+ dest.writeBoolean(mHidden);
+ }
+
+ private ModuleInfo(Parcel source) {
+ mName = source.readString();
+ mPackageName = source.readString();
+ mHidden = source.readBoolean();
+ }
+
+ public static final Parcelable.Creator<ModuleInfo> CREATOR =
+ new Parcelable.Creator<ModuleInfo>() {
+ public ModuleInfo createFromParcel(Parcel source) {
+ return new ModuleInfo(source);
+ }
+ public ModuleInfo[] newArray(int size) {
+ return new ModuleInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 34cdfee..566017b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -220,6 +220,12 @@
/** @hide */
@IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ModuleInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
GET_META_DATA,
})
@Retention(RetentionPolicy.SOURCE)
@@ -3467,6 +3473,35 @@
@ComponentInfoFlags int flags) throws NameNotFoundException;
/**
+ * Retrieve information for a particular module.
+ *
+ * @param packageName The name of the module.
+ * @param flags Additional option flags to modify the data returned.
+ * @return A {@link ModuleInfo} object containing information about the
+ * module.
+ * @throws NameNotFoundException if a module with the given name cannot be
+ * found on the system.
+ */
+ public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException(
+ "getModuleInfo not implemented in subclass");
+ }
+
+ /**
+ * Return a List of all modules that are installed.
+ *
+ * @param flags Additional option flags to modify the data returned.
+ * @return A {@link List} of {@link ModuleInfo} objects, one for each installed
+ * module, containing information about the module. In the unlikely case
+ * there are no installed modules, an empty list is returned.
+ */
+ public @NonNull List<ModuleInfo> getInstalledModules(@ModuleInfoFlags int flags) {
+ throw new UnsupportedOperationException(
+ "getInstalledModules not implemented in subclass");
+ }
+
+ /**
* Return a List of all packages that are installed for the current user.
*
* @param flags Additional option flags to modify the data returned.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c437dde..eae1aa5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2283,7 +2283,8 @@
static final String[] DATA_CONNECTION_NAMES = {
"none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
"1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
- "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
+ "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "nr",
+ "other"
};
public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
@@ -4730,7 +4731,7 @@
sb.append("\n ");
sb.append(prefix);
didOne = true;
- sb.append(DATA_CONNECTION_NAMES[i]);
+ sb.append(i < DATA_CONNECTION_NAMES.length ? DATA_CONNECTION_NAMES[i] : "ERROR");
sb.append(" ");
formatTimeMs(sb, time/1000);
sb.append("(");
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 17ce79b..0a60764 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1515,11 +1515,11 @@
* background user; the result here does not distinguish between the two.
*
* <p>Note prior to Android Nougat MR1 (SDK version <= 24;
- * {@link android.os.Build.VERSION_CODES#N), this API required a system permission
+ * {@link android.os.Build.VERSION_CODES#N}, this API required a system permission
* in order to check other profile's status.
* Since Android Nougat MR1 (SDK version >= 25;
- * {@link android.os.Build.VERSION_CODES#N_MR1)), the restriction has been relaxed, and now
- * it'll accept any {@link UserHandle} within the same profile group as the caller.
+ * {@link android.os.Build.VERSION_CODES#N_MR1}), the restriction has been relaxed, and now
+ * it'll accept any {@link android.os.UserHandle} within the same profile group as the caller.
*
* @param user The user to retrieve the running state for.
*/
@@ -1544,11 +1544,11 @@
* (but is not yet fully stopped, and still running some code).
*
* <p>Note prior to Android Nougat MR1 (SDK version <= 24;
- * {@link android.os.Build.VERSION_CODES#N), this API required a system permission
+ * {@link android.os.Build.VERSION_CODES#N}, this API required a system permission
* in order to check other profile's status.
* Since Android Nougat MR1 (SDK version >= 25;
- * {@link android.os.Build.VERSION_CODES#N_MR1)), the restriction has been relaxed, and now
- * it'll accept any {@link UserHandle} within the same profile group as the caller.
+ * {@link android.os.Build.VERSION_CODES#N_MR1}), the restriction has been relaxed, and now
+ * it'll accept any {@link android.os.UserHandle} within the same profile group as the caller.
*
* @param user The user to retrieve the running state for.
*/
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 7fd0a4b..f136cd6 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -398,6 +398,8 @@
argsForZygote.add("--mount-external-write");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
argsForZygote.add("--mount-external-full");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
+ argsForZygote.add("--mount-external-installer");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a8726e9..cd991cc 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1275,7 +1275,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size,
CancellationSignal signal) throws FileNotFoundException {
return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal);
@@ -1307,7 +1307,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
String mimeType, String displayName) throws FileNotFoundException {
return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName);
@@ -1345,7 +1345,7 @@
}
}
- /** @removed */
+ @Deprecated
public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri,
Uri childDocumentUri) throws FileNotFoundException {
return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri);
@@ -1382,7 +1382,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Uri renameDocument(ContentResolver content, Uri documentUri,
String displayName) throws FileNotFoundException {
return renameDocument((ContentInterface) content, documentUri, displayName);
@@ -1410,7 +1410,7 @@
}
}
- /** @removed */
+ @Deprecated
public static boolean deleteDocument(ContentResolver content, Uri documentUri)
throws FileNotFoundException {
return deleteDocument((ContentInterface) content, documentUri);
@@ -1441,7 +1441,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri,
Uri targetParentDocumentUri) throws FileNotFoundException {
return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri);
@@ -1474,7 +1474,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri,
Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri,
@@ -1508,7 +1508,7 @@
}
}
- /** @removed */
+ @Deprecated
public static boolean removeDocument(ContentResolver content, Uri documentUri,
Uri parentDocumentUri) throws FileNotFoundException {
return removeDocument((ContentInterface) content, documentUri, parentDocumentUri);
@@ -1531,7 +1531,7 @@
}
}
- /** @removed */
+ @Deprecated
public static void ejectRoot(ContentResolver content, Uri rootUri) {
ejectRoot((ContentInterface) content, rootUri);
}
@@ -1581,7 +1581,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri)
throws FileNotFoundException {
return getDocumentMetadata((ContentInterface) content, documentUri);
@@ -1618,7 +1618,7 @@
}
}
- /** @removed */
+ @Deprecated
public static Path findDocumentPath(ContentResolver content, Uri treeUri)
throws FileNotFoundException {
return findDocumentPath((ContentInterface) content, treeUri);
@@ -1697,7 +1697,7 @@
}
}
- /** @removed */
+ @Deprecated
public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri,
Bundle options) throws FileNotFoundException {
return createWebLinkIntent((ContentInterface) content, uri, options);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 93a5950..cbcc492 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7867,6 +7867,24 @@
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
/**
+ * Control whether Trust Agents are in active unlock or extend unlock mode.
+ * @hide
+ */
+ public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock";
+
+ private static final Validator TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
+ /**
+ * Control whether the screen locks when trust is lost.
+ * @hide
+ */
+ public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost";
+
+ private static final Validator LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
+ /**
* Control whether Night display is currently activated.
* @hide
*/
@@ -8382,6 +8400,8 @@
ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
NOTIFICATION_NEW_INTERRUPTION_MODEL,
+ TRUST_AGENTS_EXTEND_UNLOCK,
+ LOCK_SCREEN_WHEN_TRUST_LOST,
};
/**
@@ -8543,6 +8563,8 @@
VALIDATORS.put(USER_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
VALIDATORS.put(NOTIFICATION_NEW_INTERRUPTION_MODEL, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR);
+ VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
}
/**
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index d4edde9..13ac9ff 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -259,7 +259,8 @@
* Returns the source string that was saved during construction.
*
* @return the source string that was saved during construction
- * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
+ * @see #ImageSpan(Drawable, String)
+ * @see #ImageSpan(Context, Uri)
*/
@Nullable
public String getSource() {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index b59d8c7..a86abe5 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -2588,6 +2588,38 @@
}
/**
+ * Returns the original raw X coordinate of this event. For touch
+ * events on the screen, this is the original location of the event
+ * on the screen, before it had been adjusted for the containing window
+ * and views.
+ *
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ *
+ * @see #getX(int)
+ * @see #AXIS_X
+ */
+ public float getRawX(int pointerIndex) {
+ return nativeGetRawAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
+ }
+
+ /**
+ * Returns the original raw Y coordinate of this event. For touch
+ * events on the screen, this is the original location of the event
+ * on the screen, before it had been adjusted for the containing window
+ * and views.
+ *
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ *
+ * @see #getY(int)
+ * @see #AXIS_Y
+ */
+ public float getRawY(int pointerIndex) {
+ return nativeGetRawAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
+ }
+
+ /**
* Return the precision of the X coordinates being reported. You can
* multiply this number with {@link #getX} to find the actual hardware
* value of the X coordinate.
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 1889692..cc0264a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -31,7 +31,9 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
+import android.util.TimeUtils;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
@@ -98,6 +100,12 @@
*/
public static final int STATE_DISABLED = 3;
+ /**
+ * Handler message used to flush the buffer.
+ */
+ private static final int MSG_FLUSH = 1;
+
+
private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
/**
@@ -106,6 +114,12 @@
// TODO(b/111276913): use settings
private static final int MAX_BUFFER_SIZE = 100;
+ /**
+ * Frequency the buffer is flushed if stale.
+ */
+ // TODO(b/111276913): use settings
+ private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
@NonNull
private final AtomicBoolean mDisabled = new AtomicBoolean();
@@ -136,6 +150,9 @@
// held at the Application level
private final Handler mHandler;
+ // Used just for debugging purposes (on dump)
+ private long mNextFlush;
+
/** @hide */
public ContentCaptureManager(@NonNull Context context,
@Nullable IContentCaptureManager service) {
@@ -207,9 +224,17 @@
mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
}
mEvents.add(event);
+
final int numberEvents = mEvents.size();
- if (numberEvents < MAX_BUFFER_SIZE && !forceFlush) {
- // Buffering events, return right away...
+
+ // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
+ // buffered (either total or per autofillid). For
+ // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
+ // "a" and "b" then send "abc".
+ final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+
+ if (bufferEvent && !forceFlush) {
+ handleScheduleFlush();
return;
}
@@ -236,10 +261,38 @@
return;
}
+ handleForceFlush();
+ }
+
+ private void handleScheduleFlush() {
+ if (mHandler.hasMessages(MSG_FLUSH)) {
+ // "Renew" the flush message by removing the previous one
+ mHandler.removeMessages(MSG_FLUSH);
+ }
+ mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+ if (VERBOSE) {
+ Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+ }
+ mHandler.sendMessageDelayed(
+ obtainMessage(ContentCaptureManager::handleFlushIfNeeded, this).setWhat(MSG_FLUSH),
+ FLUSHING_FREQUENCY_MS);
+ }
+
+ private void handleFlushIfNeeded() {
+ if (mEvents.isEmpty()) {
+ if (VERBOSE) Log.v(TAG, "Nothing to flush");
+ return;
+ }
+ handleForceFlush();
+ }
+
+ private void handleForceFlush() {
+ final int numberEvents = mEvents.size();
try {
if (DEBUG) {
Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
}
+ mHandler.removeMessages(MSG_FLUSH);
mService.sendEvents(mContext.getUserId(), mId, mEvents);
// TODO(b/111276913): decide whether we should clear or set it to null, as each has
// its own advantages: clearing will save extra allocations while the session is
@@ -307,6 +360,7 @@
mApplicationToken = null;
mComponentName = null;
mEvents = null;
+ mHandler.removeMessages(MSG_FLUSH);
}
/**
@@ -443,7 +497,7 @@
pw.print(prefix2); pw.print("component name: ");
pw.println(mComponentName.flattenToShortString());
}
- if (mEvents != null) {
+ if (mEvents != null && !mEvents.isEmpty()) {
final int numberEvents = mEvents.size();
pw.print(prefix2); pw.print("buffered events: "); pw.print(numberEvents);
pw.print('/'); pw.println(MAX_BUFFER_SIZE);
@@ -455,6 +509,9 @@
pw.println();
}
}
+ pw.print(prefix2); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+ pw.print(prefix2); pw.print("next flush: ");
+ TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
}
}
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 3842f66..4da3391 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -445,8 +445,18 @@
}
}
+ public boolean hasProcess(String procName) {
+ final int NSRC = mSources.size();
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ if (mSources.keyAt(isrc).mProcess.equals(procName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
- long now, long totalTime, boolean dumpDetails, boolean dumpAll) {
+ long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
if (dumpAll) {
pw.print(prefix);
pw.print("mNumActive=");
@@ -456,6 +466,9 @@
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
+ if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
+ continue;
+ }
pw.print(prefixInner);
pw.print("<- ");
pw.print(key.mProcess);
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 19d8a83..9ee583a 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -1396,10 +1396,10 @@
return as;
}
- // See b/118826162 -- to avoid logspaming, we rate limit the WTF.
- private static final long INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS = 10_000L;
- private long mNextInverseProcStateWtfUptime;
- private int mSkippedInverseProcStateWtfCount;
+ // See b/118826162 -- to avoid logspaming, we rate limit the warnings.
+ private static final long INVERSE_PROC_STATE_WARNING_MIN_INTERVAL_MS = 10_000L;
+ private long mNextInverseProcStateWarningUptime;
+ private int mSkippedInverseProcStateWarningCount;
public void updateTrackingAssociationsLocked(int curSeq, long now) {
final int NUM = mTrackingAssociations.size();
@@ -1423,18 +1423,19 @@
act.stopActive(now);
if (act.mProcState < procState) {
final long nowUptime = SystemClock.uptimeMillis();
- if (mNextInverseProcStateWtfUptime > nowUptime) {
- mSkippedInverseProcStateWtfCount++;
+ if (mNextInverseProcStateWarningUptime > nowUptime) {
+ mSkippedInverseProcStateWarningCount++;
} else {
// TODO We still see it during boot related to GMS-core.
// b/118826162
- Slog.wtf(TAG, "Tracking association " + act + " whose proc state "
+ Slog.w(TAG, "Tracking association " + act + " whose proc state "
+ act.mProcState + " is better than process " + proc
+ " proc state " + procState
- + " (" + mSkippedInverseProcStateWtfCount + " skipped)");
- mSkippedInverseProcStateWtfCount = 0;
- mNextInverseProcStateWtfUptime =
- nowUptime + INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS;
+ + " (" + mSkippedInverseProcStateWarningCount
+ + " skipped)");
+ mSkippedInverseProcStateWarningCount = 0;
+ mNextInverseProcStateWarningUptime =
+ nowUptime + INVERSE_PROC_STATE_WARNING_MIN_INTERVAL_MS;
}
}
}
@@ -1474,6 +1475,7 @@
final int NSRVS = pkgState.mServices.size();
final int NASCS = pkgState.mAssociations.size();
final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ boolean onlyAssociations = false;
if (!pkgMatch) {
boolean procMatch = false;
for (int iproc = 0; iproc < NPROCS; iproc++) {
@@ -1484,7 +1486,18 @@
}
}
if (!procMatch) {
- continue;
+ // Check if this app has any associations with the requested
+ // package, so that if so we print those.
+ for (int iasc = 0; iasc < NASCS; iasc++) {
+ AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+ if (asc.hasProcess(reqPackage)) {
+ onlyAssociations = true;
+ break;
+ }
+ }
+ if (!onlyAssociations) {
+ continue;
+ }
}
}
if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
@@ -1502,7 +1515,7 @@
pw.print(vers);
pw.println(":");
}
- if ((section & REPORT_PKG_PROC_STATS) != 0) {
+ if ((section & REPORT_PKG_PROC_STATS) != 0 && !onlyAssociations) {
if (!dumpSummary || dumpAll) {
for (int iproc = 0; iproc < NPROCS; iproc++) {
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
@@ -1549,7 +1562,7 @@
now, totalTime);
}
}
- if ((section & REPORT_PKG_SVC_STATS) != 0) {
+ if ((section & REPORT_PKG_SVC_STATS) != 0 && !onlyAssociations) {
for (int isvc = 0; isvc < NSRVS; isvc++) {
ServiceState svc = pkgState.mServices.valueAt(isvc);
if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
@@ -1578,7 +1591,9 @@
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
- continue;
+ if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
+ continue;
+ }
}
if (activeOnly && !asc.isInUse()) {
pw.print(" (Not active association: ");
@@ -1596,7 +1611,8 @@
pw.print(" Process: ");
pw.println(asc.getProcessName());
asc.dumpStats(pw, " ", " ", " ",
- now, totalTime, dumpDetails, dumpAll);
+ now, totalTime, onlyAssociations ? reqPackage : null,
+ dumpDetails, dumpAll);
}
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 65213c0..65b9fad 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -81,6 +81,11 @@
public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ;
/** Read-write external storage should be mounted. */
public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE;
+ /**
+ * Mount mode for package installers which should give them access to
+ * all obb dirs in addition to their package sandboxes
+ */
+ public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
/** Read-write external storage should be mounted instead of package sandbox */
public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4a94ec4..f182c4d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -656,7 +656,9 @@
mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
} else if (arg.equals("--mount-external-full")) {
mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
- } else if (arg.equals("--query-abi-list")) {
+ } else if (arg.equals("--mount-external-installer")) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
+ } else if (arg.equals("--query-abi-list")) {
abiListQuery = true;
} else if (arg.equals("--get-pid")) {
pidQuery = true;
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 151901b..470533f2 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -326,4 +326,8 @@
throw ExceptionUtils.propagate(e);
}
}
+
+ public static @NonNull <T> List<T> defeatNullable(@Nullable List<T> val) {
+ return (val != null) ? val : Collections.emptyList();
+ }
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 2e674a5..841e5b6 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -67,6 +67,7 @@
private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
private static final int ALLOW_OEM_PERMISSIONS = 0x20;
private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x40;
+ private static final int ALLOW_ASSOCIATIONS = 0x80;
private static final int ALLOW_ALL = ~0;
// property for runtime configuration differentiation
@@ -195,6 +196,12 @@
final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
+ // Allowed associations between applications. If there are any entries
+ // for an app, those are the only associations allowed; otherwise, all associations
+ // are allowed. Allowing an association from app A to app B means app A can not
+ // associate with any other apps, but does not limit what apps B can associate with.
+ final ArrayMap<String, ArraySet<String>> mAllowedAssociations = new ArrayMap<>();
+
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
@@ -320,6 +327,10 @@
return Collections.emptyMap();
}
+ public ArrayMap<String, ArraySet<String>> getAllowedAssociations() {
+ return mAllowedAssociations;
+ }
+
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
@@ -329,8 +340,9 @@
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
- // Vendors are only allowed to customze libs, features and privapp permissions
- int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
+ // Vendors are only allowed to customize these
+ int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
+ | ALLOW_ASSOCIATIONS;
if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
@@ -359,8 +371,8 @@
odmPermissionFlag);
}
- // Allow OEM to customize features and OEM permissions
- int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
+ // Allow OEM to customize these
+ int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
@@ -423,6 +435,11 @@
}
}
+ private void logNotAllowedInPartition(String name, File permFile, XmlPullParser parser) {
+ Slog.w(TAG, "<" + name + "> not allowed in partition of "
+ + permFile + " at " + parser.getPositionDescription());
+ }
+
private void readPermissionsFromXml(File permFile, int permissionFlag) {
FileReader permReader = null;
try {
@@ -453,14 +470,17 @@
+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
- boolean allowAll = permissionFlag == ALLOW_ALL;
- boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
- boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
- boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
- boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
- boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
- boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
- boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
+ final boolean allowAll = permissionFlag == ALLOW_ALL;
+ final boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
+ final boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
+ final boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
+ final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
+ final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS)
+ != 0;
+ final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
+ final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING)
+ != 0;
+ final boolean allowAssociations = (permissionFlag & ALLOW_ASSOCIATIONS) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -468,297 +488,425 @@
}
String name = parser.getName();
- if ("group".equals(name) && allowAll) {
- String gidStr = parser.getAttributeValue(null, "gid");
- if (gidStr != null) {
- int gid = android.os.Process.getGidForName(gidStr);
- mGlobalGids = appendInt(mGlobalGids, gid);
- } else {
- Slog.w(TAG, "<group> without gid in " + permFile + " at "
- + parser.getPositionDescription());
- }
-
+ if (name == null) {
XmlUtils.skipCurrentTag(parser);
continue;
- } else if ("permission".equals(name) && allowPermissions) {
- String perm = parser.getAttributeValue(null, "name");
- if (perm == null) {
- Slog.w(TAG, "<permission> without name in " + permFile + " at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- perm = perm.intern();
- readPermission(parser, perm);
-
- } else if ("assign-permission".equals(name) && allowPermissions) {
- String perm = parser.getAttributeValue(null, "name");
- if (perm == null) {
- Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- String uidStr = parser.getAttributeValue(null, "uid");
- if (uidStr == null) {
- Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- int uid = Process.getUidForName(uidStr);
- if (uid < 0) {
- Slog.w(TAG, "<assign-permission> with unknown uid \""
- + uidStr + " in " + permFile + " at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- perm = perm.intern();
- ArraySet<String> perms = mSystemPermissions.get(uid);
- if (perms == null) {
- perms = new ArraySet<String>();
- mSystemPermissions.put(uid, perms);
- }
- perms.add(perm);
- XmlUtils.skipCurrentTag(parser);
-
- } else if ("split-permission".equals(name) && allowPermissions) {
- readSplitPermission(parser, permFile);
- } else if ("library".equals(name) && allowLibs) {
- String lname = parser.getAttributeValue(null, "name");
- String lfile = parser.getAttributeValue(null, "file");
- String ldependency = parser.getAttributeValue(null, "dependency");
- if (lname == null) {
- Slog.w(TAG, "<library> without name in " + permFile + " at "
- + parser.getPositionDescription());
- } else if (lfile == null) {
- Slog.w(TAG, "<library> without file in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- //Log.i(TAG, "Got library " + lname + " in " + lfile);
- SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
- ldependency == null ? new String[0] : ldependency.split(":"));
- mSharedLibraries.put(lname, entry);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else if ("feature".equals(name) && allowFeatures) {
- String fname = parser.getAttributeValue(null, "name");
- int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
- boolean allowed;
- if (!lowRam) {
- allowed = true;
- } else {
- String notLowRam = parser.getAttributeValue(null, "notLowRam");
- allowed = !"true".equals(notLowRam);
- }
- if (fname == null) {
- Slog.w(TAG, "<feature> without name in " + permFile + " at "
- + parser.getPositionDescription());
- } else if (allowed) {
- addFeature(fname, fversion);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("unavailable-feature".equals(name) && allowFeatures) {
- String fname = parser.getAttributeValue(null, "name");
- if (fname == null) {
- Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- mUnavailableFeatures.add(fname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mAllowInPowerSaveExceptIdle.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("allow-in-power-save".equals(name) && allowAll) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- mAllowInPowerSave.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("allow-in-data-usage-save".equals(name) && allowAll) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<allow-in-data-usage-save> without package in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mAllowInDataUsageSave.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("allow-unthrottled-location".equals(name) && allowAll) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<allow-unthrottled-location> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mAllowUnthrottledLocation.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("allow-implicit-broadcast".equals(name) && allowAll) {
- String action = parser.getAttributeValue(null, "action");
- if (action == null) {
- Slog.w(TAG, "<allow-implicit-broadcast> without action in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mAllowImplicitBroadcasts.add(action);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("app-link".equals(name) && allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<app-link> without package in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- mLinkedApps.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mSystemUserWhitelistedApps.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mSystemUserBlacklistedApps.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- String clsname = parser.getAttributeValue(null, "class");
- if (pkgname == null) {
- Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
- + " at " + parser.getPositionDescription());
- } else if (clsname == null) {
- Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) {
- String serviceName = parser.getAttributeValue(null, "service");
- if (serviceName == null) {
- Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- ComponentName cn = ComponentName.unflattenFromString(serviceName);
- if (cn == null) {
- Slog.w(TAG,
- "<backup-transport-whitelisted-service> with invalid service name "
- + serviceName + " in "+ permFile
- + " at " + parser.getPositionDescription());
- } else {
- mBackupTransportWhitelist.add(cn);
- }
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)
- && allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");
- if (pkgname == null || carrierPkgname == null) {
- Slog.w(TAG, "<disabled-until-used-preinstalled-carrier-associated-app"
- + " without package or carrierAppPackage in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- List<String> associatedPkgs =
- mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
- carrierPkgname);
- if (associatedPkgs == null) {
- associatedPkgs = new ArrayList<>();
- mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
- carrierPkgname, associatedPkgs);
- }
- associatedPkgs.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("disabled-until-used-preinstalled-carrier-app".equals(name)
- && allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG,
- "<disabled-until-used-preinstalled-carrier-app> without "
- + "package in " + permFile + " at "
+ }
+ switch (name) {
+ case "group": {
+ if (allowAll) {
+ String gidStr = parser.getAttributeValue(null, "gid");
+ if (gidStr != null) {
+ int gid = android.os.Process.getGidForName(gidStr);
+ mGlobalGids = appendInt(mGlobalGids, gid);
+ } else {
+ Slog.w(TAG, "<" + name + "> without gid in " + permFile + " at "
+ parser.getPositionDescription());
- } else {
- mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
- // privapp permissions from system, vendor, product and product_services
- // partitions are stored separately. This is to prevent xml files in the vendor
- // partition from granting permissions to priv apps in the system partition and
- // vice versa.
- boolean vendor = permFile.toPath().startsWith(
- Environment.getVendorDirectory().toPath() + "/")
- || permFile.toPath().startsWith(
- Environment.getOdmDirectory().toPath() + "/");
- boolean product = permFile.toPath().startsWith(
- Environment.getProductDirectory().toPath() + "/");
- boolean productServices = permFile.toPath().startsWith(
- Environment.getProductServicesDirectory().toPath() + "/");
- if (vendor) {
- readPrivAppPermissions(parser, mVendorPrivAppPermissions,
- mVendorPrivAppDenyPermissions);
- } else if (product) {
- readPrivAppPermissions(parser, mProductPrivAppPermissions,
- mProductPrivAppDenyPermissions);
- } else if (productServices) {
- readPrivAppPermissions(parser, mProductServicesPrivAppPermissions,
- mProductServicesPrivAppDenyPermissions);
- } else {
- readPrivAppPermissions(parser, mPrivAppPermissions,
- mPrivAppDenyPermissions);
- }
- } else if ("oem-permissions".equals(name) && allowOemPermissions) {
- readOemPermissions(parser);
- } else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<hidden-api-whitelisted-app> without package in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mHiddenApiPackageWhitelist.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } else {
- Slog.w(TAG, "Tag " + name + " is unknown or not allowed in "
- + permFile.getParent());
- XmlUtils.skipCurrentTag(parser);
- continue;
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "permission": {
+ if (allowPermissions) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ perm = perm.intern();
+ readPermission(parser, perm);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ } break;
+ case "assign-permission": {
+ if (allowPermissions) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG, "<" + name + "> without name in " + permFile
+ + " at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ String uidStr = parser.getAttributeValue(null, "uid");
+ if (uidStr == null) {
+ Slog.w(TAG, "<" + name + "> without uid in " + permFile
+ + " at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ int uid = Process.getUidForName(uidStr);
+ if (uid < 0) {
+ Slog.w(TAG, "<" + name + "> with unknown uid \""
+ + uidStr + " in " + permFile + " at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ perm = perm.intern();
+ ArraySet<String> perms = mSystemPermissions.get(uid);
+ if (perms == null) {
+ perms = new ArraySet<String>();
+ mSystemPermissions.put(uid, perms);
+ }
+ perms.add(perm);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "split-permission": {
+ if (allowPermissions) {
+ readSplitPermission(parser, permFile);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ } break;
+ case "library": {
+ if (allowLibs) {
+ String lname = parser.getAttributeValue(null, "name");
+ String lfile = parser.getAttributeValue(null, "file");
+ String ldependency = parser.getAttributeValue(null, "dependency");
+ if (lname == null) {
+ Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ + parser.getPositionDescription());
+ } else if (lfile == null) {
+ Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+ + parser.getPositionDescription());
+ } else {
+ //Log.i(TAG, "Got library " + lname + " in " + lfile);
+ SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
+ ldependency == null ? new String[0] : ldependency.split(":"));
+ mSharedLibraries.put(lname, entry);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "feature": {
+ if (allowFeatures) {
+ String fname = parser.getAttributeValue(null, "name");
+ int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
+ boolean allowed;
+ if (!lowRam) {
+ allowed = true;
+ } else {
+ String notLowRam = parser.getAttributeValue(null, "notLowRam");
+ allowed = !"true".equals(notLowRam);
+ }
+ if (fname == null) {
+ Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ + parser.getPositionDescription());
+ } else if (allowed) {
+ addFeature(fname, fversion);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "unavailable-feature": {
+ if (allowFeatures) {
+ String fname = parser.getAttributeValue(null, "name");
+ if (fname == null) {
+ Slog.w(TAG, "<" + name + "> without name in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mUnavailableFeatures.add(fname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "allow-in-power-save-except-idle": {
+ if (allowAll) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowInPowerSaveExceptIdle.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "allow-in-power-save": {
+ if (allowAll) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowInPowerSave.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "allow-in-data-usage-save": {
+ if (allowAll) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowInDataUsageSave.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "allow-unthrottled-location": {
+ if (allowAll) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowUnthrottledLocation.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "allow-implicit-broadcast": {
+ if (allowAll) {
+ String action = parser.getAttributeValue(null, "action");
+ if (action == null) {
+ Slog.w(TAG, "<" + name + "> without action in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowImplicitBroadcasts.add(action);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "app-link": {
+ if (allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mLinkedApps.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "system-user-whitelisted-app": {
+ if (allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mSystemUserWhitelistedApps.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "system-user-blacklisted-app": {
+ if (allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mSystemUserBlacklistedApps.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "default-enabled-vr-app": {
+ if (allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ String clsname = parser.getAttributeValue(null, "class");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else if (clsname == null) {
+ Slog.w(TAG, "<" + name + "> without class in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "backup-transport-whitelisted-service": {
+ if (allowFeatures) {
+ String serviceName = parser.getAttributeValue(null, "service");
+ if (serviceName == null) {
+ Slog.w(TAG, "<" + name + "> without service in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ ComponentName cn = ComponentName.unflattenFromString(serviceName);
+ if (cn == null) {
+ Slog.w(TAG, "<" + name + "> with invalid service name "
+ + serviceName + " in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mBackupTransportWhitelist.add(cn);
+ }
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "disabled-until-used-preinstalled-carrier-associated-app": {
+ if (allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ String carrierPkgname = parser.getAttributeValue(null,
+ "carrierAppPackage");
+ if (pkgname == null || carrierPkgname == null) {
+ Slog.w(TAG, "<" + name
+ + "> without package or carrierAppPackage in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ List<String> associatedPkgs =
+ mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
+ carrierPkgname);
+ if (associatedPkgs == null) {
+ associatedPkgs = new ArrayList<>();
+ mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
+ carrierPkgname, associatedPkgs);
+ }
+ associatedPkgs.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "disabled-until-used-preinstalled-carrier-app": {
+ if (allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG,
+ "<" + name + "> without "
+ + "package in " + permFile + " at "
+ + parser.getPositionDescription());
+ } else {
+ mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "privapp-permissions": {
+ if (allowPrivappPermissions) {
+ // privapp permissions from system, vendor, product and product_services
+ // partitions are stored separately. This is to prevent xml files in
+ // the vendor partition from granting permissions to priv apps in the
+ // system partition and vice versa.
+ boolean vendor = permFile.toPath().startsWith(
+ Environment.getVendorDirectory().toPath() + "/")
+ || permFile.toPath().startsWith(
+ Environment.getOdmDirectory().toPath() + "/");
+ boolean product = permFile.toPath().startsWith(
+ Environment.getProductDirectory().toPath() + "/");
+ boolean productServices = permFile.toPath().startsWith(
+ Environment.getProductServicesDirectory().toPath() + "/");
+ if (vendor) {
+ readPrivAppPermissions(parser, mVendorPrivAppPermissions,
+ mVendorPrivAppDenyPermissions);
+ } else if (product) {
+ readPrivAppPermissions(parser, mProductPrivAppPermissions,
+ mProductPrivAppDenyPermissions);
+ } else if (productServices) {
+ readPrivAppPermissions(parser, mProductServicesPrivAppPermissions,
+ mProductServicesPrivAppDenyPermissions);
+ } else {
+ readPrivAppPermissions(parser, mPrivAppPermissions,
+ mPrivAppDenyPermissions);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ } break;
+ case "oem-permissions": {
+ if (allowOemPermissions) {
+ readOemPermissions(parser);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ } break;
+ case "hidden-api-whitelisted-app": {
+ if (allowApiWhitelisting) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mHiddenApiPackageWhitelist.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ case "allow-association": {
+ if (allowAssociations) {
+ String target = parser.getAttributeValue(null, "target");
+ if (target == null) {
+ Slog.w(TAG, "<" + name + "> without target in " + permFile
+ + " at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ String allowed = parser.getAttributeValue(null, "allowed");
+ if (allowed == null) {
+ Slog.w(TAG, "<" + name + "> without allowed in " + permFile
+ + " at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ target = target.intern();
+ allowed = allowed.intern();
+ ArraySet<String> associations = mAllowedAssociations.get(target);
+ if (associations == null) {
+ associations = new ArraySet<>();
+ mAllowedAssociations.put(target, associations);
+ }
+ Slog.i(TAG, "Adding association: " + target + " <- " + allowed);
+ associations.add(allowed);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
+ default: {
+ Slog.w(TAG, "Tag " + name + " is unknown in "
+ + permFile + " at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ } break;
}
}
} catch (XmlPullParserException e) {
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 6ebf35c..a54571b 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -36,7 +36,7 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SafeUnref));
}
- static jlong CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
+ static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
return reinterpret_cast<jlong>(SkColorFilter::MakeModeFilter(srcColor, mode).release());
}
@@ -61,8 +61,8 @@
{"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer }
};
-static const JNINativeMethod porterduff_methods[] = {
- { "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter },
+static const JNINativeMethod blendmode_methods[] = {
+ { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter },
};
static const JNINativeMethod lighting_methods[] = {
@@ -76,8 +76,10 @@
int register_android_graphics_ColorFilter(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
NELEM(colorfilter_methods));
- android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", porterduff_methods,
- NELEM(porterduff_methods));
+ android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", blendmode_methods,
+ NELEM(blendmode_methods));
+ android::RegisterMethodsOrDie(env, "android/graphics/BlendModeColorFilter", blendmode_methods,
+ NELEM(blendmode_methods));
android::RegisterMethodsOrDie(env, "android/graphics/LightingColorFilter", lighting_methods,
NELEM(lighting_methods));
android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index c249e20..df24bc4 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -845,12 +845,23 @@
static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
- static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
- static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+ static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
- static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
+ static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
+ static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+ static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch");
+ static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch");
+ static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch");
+ static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch");
+ static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch");
+ static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch");
+ static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch");
+ static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch");
+ static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch");
+ static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch");
+ static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch");
SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 4f8bbc1..3329e20 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -919,7 +919,7 @@
return IPCThreadState::self()->clearCallingWorkSource();
}
-static void android_os_Binder_restoreCallingWorkSource(long token)
+static void android_os_Binder_restoreCallingWorkSource(jlong token)
{
IPCThreadState::self()->restoreCallingWorkSource(token);
}
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index b708735..24bafca 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -35,7 +35,6 @@
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
-using android::bpf::hasBpfSupport;
using android::bpf::parseBpfNetworkStatsDetail;
using android::bpf::stats_line;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4aa88e7..7032081 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -99,7 +99,8 @@
MOUNT_EXTERNAL_DEFAULT = 1,
MOUNT_EXTERNAL_READ = 2,
MOUNT_EXTERNAL_WRITE = 3,
- MOUNT_EXTERNAL_FULL = 4,
+ MOUNT_EXTERNAL_INSTALLER = 4,
+ MOUNT_EXTERNAL_FULL = 5,
};
// Must match values in com.android.internal.os.Zygote.
@@ -446,6 +447,22 @@
return true;
}
+static bool bindMount(const std::string& sourceDir, const std::string& targetDir,
+ std::string* error_msg) {
+ if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(),
+ nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
+ sourceDir.c_str(), targetDir.c_str(), strerror(errno));
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(),
+ nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str());
+ return false;
+ }
+ return true;
+}
+
static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
const std::string& mntTargetRoot, const std::string& packageName,
const char* dirName, std::string* error_msg) {
@@ -453,22 +470,12 @@
mntSourceRoot.c_str(), dirName, packageName.c_str());
std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
mntTargetRoot.c_str(), dirName, packageName.c_str());
- if (TEMP_FAILURE_RETRY(mount(mntSourceDir.c_str(), mntTargetDir.c_str(),
- nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
- mntSourceDir.c_str(), mntTargetDir.c_str(), strerror(errno));
- return false;
- }
- if (TEMP_FAILURE_RETRY(mount(nullptr, mntTargetDir.c_str(),
- nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", mntTargetDir.c_str());
- return false;
- }
- return true;
+ return bindMount(mntSourceDir, mntTargetDir, error_msg);
}
static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
- const std::vector<std::string>& volumeLabels, userid_t userId, std::string* error_msg) {
+ const std::vector<std::string>& volumeLabels, bool mountAllObbs,
+ userid_t userId, std::string* error_msg) {
for (auto& label : volumeLabels) {
std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
@@ -479,7 +486,14 @@
for (auto& package : packageNames) {
mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
- mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+ if (!mountAllObbs) {
+ mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+ }
+ }
+ if (mountAllObbs) {
+ StringAppendF(&mntSource, "/Android/obb");
+ StringAppendF(&mntTarget, "/Android/obb");
+ bindMount(mntSource, mntTarget, error_msg);
}
}
return true;
@@ -500,7 +514,7 @@
storageSource = "/mnt/runtime/read";
} else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
storageSource = "/mnt/runtime/write";
- } else if (mount_mode != MOUNT_EXTERNAL_FULL && !force_mount_namespace) {
+ } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
// Sane default of no storage visible
return true;
}
@@ -568,12 +582,28 @@
pkgSandboxDir.c_str(), strerror(errno));
return false;
}
+ if (access("/storage/obb_mount", F_OK) == 0) {
+ if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
+ remove("/storage/obb_mount");
+ }
+ } else {
+ if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+ int fd = TEMP_FAILURE_RETRY(open("/storage/obb_mount",
+ O_RDWR | O_CREAT, 0660));
+ if (fd == -1) {
+ *error_msg = CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
+ strerror(errno));
+ return false;
+ }
+ close(fd);
+ }
+ }
// If the sandbox was already created by vold, only then set up the bind mounts for
// pkg specific directories. Otherwise, leave as is and bind mounts will be taken
// care of by vold later.
if (sandboxAlreadyCreated) {
if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
- user_id, error_msg)) {
+ mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, error_msg)) {
return false;
}
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 514f306..3e1c5a3 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -82,5 +82,7 @@
// OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
SETTINGS_WIFI_DPP_ENROLLEE = 1596;
-}
+ // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
+ SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 77efbec..dca15bd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1121,10 +1121,10 @@
android:protectionLevel="normal" />
<!--Allows an app which implements the
- {@link InCallService} API to be eligible to be enabled as a calling companion app. This
- means that the Telecom framework will bind to the app's InCallService implementation when
- there are calls active. The app can use the InCallService API to view information about
- calls on the system and control these calls.
+ {@link android.telecom.InCallService InCallService} API to be eligible to be enabled as a
+ calling companion app. This means that the Telecom framework will bind to the app's
+ InCallService implementation when there are calls active. The app can use the InCallService
+ API to view information about calls on the system and control these calls.
<p>Protection level: normal
-->
<permission android:name="android.permission.CALL_COMPANION_APP"
@@ -4696,6 +4696,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.server.pm.DynamicCodeLoggingService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.PruneInstantAppsJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index d1dbd3c..5664df6 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -37,6 +38,7 @@
* Test whether Binder calls work source is propagated correctly.
*/
@LargeTest
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class BinderWorkSourceTest {
private static Context sContext;
@@ -125,8 +127,10 @@
Binder.setCallingWorkSourceUid(UID);
long token = Binder.clearCallingWorkSource();
Binder.restoreCallingWorkSource(token);
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
assertEquals(UID, mService.getIncomingWorkSourceUid());
+ // Still the same after the binder transaction.
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index bbec474..ea66ee3 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1 +1 @@
-per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com
+per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com, cbrubaker@google.com, jeffv@google.com, moltmann@google.com
diff --git a/docs/html/reference/images/graphics/blendmode_CLEAR.png b/docs/html/reference/images/graphics/blendmode_CLEAR.png
new file mode 100644
index 0000000..979782a
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_CLEAR.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_COLOR.png b/docs/html/reference/images/graphics/blendmode_COLOR.png
new file mode 100644
index 0000000..2f41bfb
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_COLOR.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_COLOR_BURN.png b/docs/html/reference/images/graphics/blendmode_COLOR_BURN.png
new file mode 100644
index 0000000..26059ce
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_COLOR_BURN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_COLOR_DODGE.png b/docs/html/reference/images/graphics/blendmode_COLOR_DODGE.png
new file mode 100644
index 0000000..922f1d9
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_COLOR_DODGE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DARKEN.png b/docs/html/reference/images/graphics/blendmode_DARKEN.png
new file mode 100644
index 0000000..6c04aa3
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DARKEN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DIFFERENCE.png b/docs/html/reference/images/graphics/blendmode_DIFFERENCE.png
new file mode 100644
index 0000000..aab2bcb
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DIFFERENCE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST.png b/docs/html/reference/images/graphics/blendmode_DST.png
new file mode 100644
index 0000000..16f96b4
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_ATOP.png b/docs/html/reference/images/graphics/blendmode_DST_ATOP.png
new file mode 100644
index 0000000..d0ae2cd
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_ATOP.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_IN.png b/docs/html/reference/images/graphics/blendmode_DST_IN.png
new file mode 100644
index 0000000..9befe27
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_IN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_OUT.png b/docs/html/reference/images/graphics/blendmode_DST_OUT.png
new file mode 100644
index 0000000..e9227e9f
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_OUT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_OVER.png b/docs/html/reference/images/graphics/blendmode_DST_OVER.png
new file mode 100644
index 0000000..015be0a
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_OVER.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_EXCLUSION.png b/docs/html/reference/images/graphics/blendmode_EXCLUSION.png
new file mode 100644
index 0000000..307dec9
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_EXCLUSION.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_HARD_LIGHT.png b/docs/html/reference/images/graphics/blendmode_HARD_LIGHT.png
new file mode 100644
index 0000000..3b62b98
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_HARD_LIGHT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_HUE.png b/docs/html/reference/images/graphics/blendmode_HUE.png
new file mode 100644
index 0000000..012bd33
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_HUE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_LIGHTEN.png b/docs/html/reference/images/graphics/blendmode_LIGHTEN.png
new file mode 100644
index 0000000..1c3be65
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_LIGHTEN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_LUMINOSITY.png b/docs/html/reference/images/graphics/blendmode_LUMINOSITY.png
new file mode 100644
index 0000000..3549082
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_LUMINOSITY.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_MODULATE.png b/docs/html/reference/images/graphics/blendmode_MODULATE.png
new file mode 100644
index 0000000..ed1b59d
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_MODULATE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_MULTIPLY.png b/docs/html/reference/images/graphics/blendmode_MULTIPLY.png
new file mode 100644
index 0000000..c8c7ccb
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_MULTIPLY.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_OVERLAY.png b/docs/html/reference/images/graphics/blendmode_OVERLAY.png
new file mode 100644
index 0000000..6962f92
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_OVERLAY.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_PLUS.png b/docs/html/reference/images/graphics/blendmode_PLUS.png
new file mode 100644
index 0000000..015fa2b
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_PLUS.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SATURATION.png b/docs/html/reference/images/graphics/blendmode_SATURATION.png
new file mode 100644
index 0000000..5ac96c3
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SATURATION.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SCREEN.png b/docs/html/reference/images/graphics/blendmode_SCREEN.png
new file mode 100644
index 0000000..d2d70d2
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SCREEN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SOFT_LIGHT.png b/docs/html/reference/images/graphics/blendmode_SOFT_LIGHT.png
new file mode 100644
index 0000000..89fbacd
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SOFT_LIGHT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC.png b/docs/html/reference/images/graphics/blendmode_SRC.png
new file mode 100644
index 0000000..990ec94
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_ATOP.png b/docs/html/reference/images/graphics/blendmode_SRC_ATOP.png
new file mode 100644
index 0000000..d574dfd
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_ATOP.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_IN.png b/docs/html/reference/images/graphics/blendmode_SRC_IN.png
new file mode 100644
index 0000000..dda45d7
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_IN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_OUT.png b/docs/html/reference/images/graphics/blendmode_SRC_OUT.png
new file mode 100644
index 0000000..f5d43c1
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_OUT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_OVER.png b/docs/html/reference/images/graphics/blendmode_SRC_OVER.png
new file mode 100644
index 0000000..b1a405b
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_OVER.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_XOR.png b/docs/html/reference/images/graphics/blendmode_XOR.png
new file mode 100644
index 0000000..31c110d
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_XOR.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 3db240b..ca9dc47 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -241,10 +241,22 @@
nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
+ /**
+ * @deprecated use {@link Canvas#drawColor(int, BlendMode)}
+ */
+ @Deprecated
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
}
+ /**
+ * Make lint happy.
+ * See {@link Canvas#drawColor(int, BlendMode)}
+ */
+ public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
+ nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
+ }
+
public void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {
throwIfHasHwBitmapInSwMode(paint);
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 4de7ca7..901c211 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -201,12 +201,21 @@
nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
+ /**
+ * @deprecated use {@link #drawColor(int, BlendMode)} instead
+ */
+ @Deprecated
@Override
public final void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
}
@Override
+ public final void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
+ nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
+ }
+
+ @Override
public final void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {
nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
diff --git a/graphics/java/android/graphics/BlendMode.java b/graphics/java/android/graphics/BlendMode.java
new file mode 100644
index 0000000..39392c8
--- /dev/null
+++ b/graphics/java/android/graphics/BlendMode.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+public enum BlendMode {
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_CLEAR.png" />
+ * <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = 0\)</p>
+ * <p>\(C_{out} = 0\)</p>
+ */
+ CLEAR(0),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC.png" />
+ * <figcaption>The source pixels replace the destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src}\)</p>
+ * <p>\(C_{out} = C_{src}\)</p>
+ */
+ SRC(1),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DST.png" />
+ * <figcaption>The source pixels are discarded, leaving the destination intact.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
+ * <p>\(C_{out} = C_{dst}\)</p>
+ */
+ DST(2),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_OVER.png" />
+ * <figcaption>The source pixels are drawn over the destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
+ * <p>\(C_{out} = C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
+ */
+ SRC_OVER(3),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_OVER.png" />
+ * <figcaption>The source pixels are drawn behind the destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{dst} + (1 - \alpha_{dst}) * \alpha_{src}\)</p>
+ * <p>\(C_{out} = C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
+ */
+ DST_OVER(4),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_IN.png" />
+ * <figcaption>Keeps the source pixels that cover the destination pixels,
+ * discards the remaining source and destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>\(C_{out} = C_{src} * \alpha_{dst}\)</p>
+ */
+ SRC_IN(5),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_IN.png" />
+ * <figcaption>Keeps the destination pixels that cover source pixels,
+ * discards the remaining source and destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>\(C_{out} = C_{dst} * \alpha_{src}\)</p>
+ */
+ DST_IN(6),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_OUT.png" />
+ * <figcaption>Keeps the source pixels that do not cover destination pixels.
+ * Discards source pixels that cover destination pixels. Discards all
+ * destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src}\)</p>
+ * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src}\)</p>
+ */
+ SRC_OUT(7),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_OUT.png" />
+ * <figcaption>Keeps the destination pixels that are not covered by source pixels.
+ * Discards destination pixels that are covered by source pixels. Discards all
+ * source pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = (1 - \alpha_{src}) * \alpha_{dst}\)</p>
+ * <p>\(C_{out} = (1 - \alpha_{src}) * C_{dst}\)</p>
+ */
+ DST_OUT(8),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_ATOP.png" />
+ * <figcaption>Discards the source pixels that do not cover destination pixels.
+ * Draws remaining source pixels over destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
+ * <p>\(C_{out} = \alpha_{dst} * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
+ */
+ SRC_ATOP(9),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_ATOP.png" />
+ * <figcaption>Discards the destination pixels that are not covered by source pixels.
+ * Draws remaining destination pixels over source pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src}\)</p>
+ * <p>\(C_{out} = \alpha_{src} * C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
+ */
+ DST_ATOP(10),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_XOR.png" />
+ * <figcaption>Discards the source and destination pixels where source pixels
+ * cover destination pixels. Draws remaining source pixels.</figcaption>
+ * </p>
+ * <p>
+ * \(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)
+ * </p>
+ * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
+ */
+ XOR(11),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_PLUS.png" />
+ * <figcaption>Adds the source pixels to the destination pixels and saturates
+ * the result.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = max(0, min(\alpha_{src} + \alpha_{dst}, 1))\)</p>
+ * <p>\(C_{out} = max(0, min(C_{src} + C_{dst}, 1))\)</p>
+ */
+ PLUS(12),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
+ * <figcaption>Multiplies the source and destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>\(C_{out} = C_{src} * C_{dst}\)</p>
+ *
+ */
+ MODULATE(13),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SCREEN.png" />
+ * <figcaption>
+ * Adds the source and destination pixels, then subtracts the
+ * source pixels multiplied by the destination.
+ * </figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>\(C_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}\)</p>
+ */
+ SCREEN(14),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_OVERLAY.png" />
+ * <figcaption>
+ * Multiplies or screens the source and destination depending on the
+ * destination color.
+ * </figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>\(\begin{equation}
+ * C_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\
+ * \alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) &
+ * otherwise \end{cases}
+ * \end{equation}\)</p>
+ */
+ OVERLAY(15),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DARKEN.png" />
+ * <figcaption>
+ * Retains the smallest component of the source and
+ * destination pixels.
+ * </figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>
+ * \(C_{out} =
+ * (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})\)
+ * </p>
+ */
+ DARKEN(16),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_LIGHTEN.png" />
+ * <figcaption>Retains the largest component of the source and
+ * destination pixel.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>
+ * \(C_{out} =
+ * (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})\)
+ * </p>
+ */
+ LIGHTEN(17),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_DODGE.png" />
+ * <figcaption>Makes destination brighter to reflect source.</figcaption>
+ * </p>
+ * <p>
+ * \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
+ * </p>
+ * <p>
+ * \begin{equation}
+ * C_{out} =
+ * \begin{cases}
+ * C_{src} * (1 - \alpha_{dst}) & C_{dst} = 0 \\
+ * C_{src} + \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = \alpha_{src} \\
+ * \alpha_{src} * min(\alpha_{dst}, C_{dst} * \alpha_{src}/(\alpha_{src} - C_{src}))
+ * + C_{src} *(1 - \alpha_{dst} + \alpha_{dst}*(1 - \alpha_{src}) & otherwise
+ * \end{cases}
+ * \end{equation}
+ * </p>
+ */
+ COLOR_DODGE(18),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_BURN.png" />
+ * <figcaption>Makes destination darker to reflect source.</figcaption>
+ * </p>
+ * <p>
+ * \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
+ * </p>
+ * <p>
+ * \begin{equation}
+ * C_{out} =
+ * \begin{cases}
+ * C_{dst} + C_{src}*(1 - \alpha_{dst}) & C_{dst} = \alpha_{dst} \\
+ * \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = 0 \\
+ * \alpha_{src}*(\alpha_{dst} - min(\alpha_{dst}, (\alpha_{dst}
+ * - C_{dst})*\alpha_{src}/C_{src}))
+ * + C_{src} * (1 - \alpha_{dst}) + \alpha_{dst}*(1-\alpha_{src}) & otherwise
+ * \end{cases}
+ * \end{equation}
+ * </p>
+ */
+ COLOR_BURN(19),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_HARD_LIGHT.png" />
+ * <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
+ * </p>
+ * <p>
+ * \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
+ * </p>
+ * <p>
+ * \begin{equation}
+ * C_{out} =
+ * \begin{cases}
+ * 2*C_{src}*C_{dst} & C_{src}*(1-\alpha_{dst}) + C_{dst}*(1-\alpha_{src}) + 2*C_{src}
+ * \leq \alpha_{src} \\
+ * \alpha_{src}*\alpha_{dst}- 2*(\alpha_{dst} - C_{dst})*(\alpha_{src} - C_{src})
+ * & otherwise
+ * \end{cases}
+ * \end{equation}
+ * </p>
+ */
+ HARD_LIGHT(20),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SOFT_LIGHT.png" />
+ * <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
+ * </p>
+ * <p>
+ * Where
+ * \begin{equation}
+ * m =
+ * \begin{cases}
+ * C_{dst} / \alpha_{dst} & \alpha_{dst} \gt 0 \\
+ * 0 & otherwise
+ * \end{cases}
+ * \end{equation}
+ * </p>
+ * <p>
+ * \begin{equation}
+ * g =
+ * \begin{cases}
+ * (16 * m * m + 4 * m) * (m - 1) + 7 * m & 4 * C_{dst} \leq \alpha_{dst} \\
+ * \sqrt m - m & otherwise
+ * \end{cases}
+ * \end{equation}
+ * </p>
+ * <p>
+ * \begin{equation}
+ * f =
+ * \begin{cases}
+ * C_{dst} * (\alpha_{src} + (2 * C_{src} - \alpha_{src}) * (1 - m))
+ * & 2 * C_{src} \leq \alpha_{src} \\
+ * C_{dst} * \alpha_{src} + \alpha_{dst} * (2 * C_{src} - \alpha_{src}) * g
+ * & otherwise
+ * \end{cases}
+ * \end{equation}
+ * </p>
+ * <p>
+ * \begin{equation}
+ * \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
+ * \end{equation}
+ * \begin{equation}
+ * C_{out} = C_{src} / \alpha_{dst} + C_{dst} / \alpha_{src} + f
+ * \end{equation}
+ * </p>
+ */
+ SOFT_LIGHT(21),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
+ * <figcaption>Subtracts darker from lighter with higher contrast.</figcaption>
+ * </p>
+ * <p>
+ * \begin{equation}
+ * \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
+ * \end{equation}
+ * </p>
+ * <p>
+ * \begin{equation}
+ * C_{out} = C_{src} + C_{dst} - 2 * min(C_{src}
+ * * \alpha_{dst}, C_{dst} * \alpha_{src})
+ * \end{equation}
+ * </p>
+ */
+ DIFFERENCE(22),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
+ * <figcaption>Subtracts darker from lighter with lower contrast.</figcaption>
+ * </p>
+ * <p>
+ * \begin{equation}
+ * \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
+ * \end{equation}
+ * </p>
+ * <p>
+ * \begin{equation}
+ * C_{out} = C_{src} + C_{dst} - 2 * C_{src} * C_{dst}
+ * \end{equation}
+ * </p>
+ */
+ EXCLUSION(23),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
+ * <figcaption>Multiplies the source and destination pixels.</figcaption>
+ * </p>
+ * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+ * <p>\(C_{out} =
+ * C_{src} * (1 - \alpha_{dst}) + C_{dst} * (1 - \alpha_{src}) + (C_{src} * C_{dst})\)
+ * </p>
+ */
+ MULTIPLY(24),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_HUE.png" />
+ * <figcaption>
+ * Replaces hue of destination with hue of source, leaving saturation
+ * and luminosity unchanged.
+ * </figcaption>
+ * </p>
+ */
+ HUE(25),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_SATURATION.png" />
+ * <figcaption>
+ * Replaces saturation of destination saturation hue of source, leaving hue and
+ * luminosity unchanged.
+ * </figcaption>
+ * </p>
+ */
+ SATURATION(26),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR.png" />
+ * <figcaption>
+ * Replaces hue and saturation of destination with hue and saturation of source,
+ * leaving luminosity unchanged.
+ * </figcaption>
+ * </p>
+ */
+ COLOR(27),
+
+ /**
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/blendmode_LUMINOSITY.png" />
+ * <figcaption>
+ * Replaces luminosity of destination with luminosity of source, leaving hue and
+ * saturation unchanged.
+ * </figcaption>
+ * </p>
+ */
+ LUMINOSITY(28);
+
+ private static final BlendMode[] BLEND_MODES = values();
+
+ /**
+ * @hide
+ */
+ public static @Nullable BlendMode fromValue(int value) {
+ for (BlendMode mode : BLEND_MODES) {
+ if (mode.mXfermode.porterDuffMode == value) {
+ return mode;
+ }
+ }
+ return null;
+ }
+
+ @NonNull
+ private final Xfermode mXfermode;
+
+ BlendMode(int mode) {
+ mXfermode = new Xfermode();
+ mXfermode.porterDuffMode = mode;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Xfermode getXfermode() {
+ return mXfermode;
+ }
+}
diff --git a/graphics/java/android/graphics/BlendModeColorFilter.java b/graphics/java/android/graphics/BlendModeColorFilter.java
new file mode 100644
index 0000000..7caeb42
--- /dev/null
+++ b/graphics/java/android/graphics/BlendModeColorFilter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+
+/**
+ * A color filter that can be used to tint the source pixels using a single
+ * color and a specific {@link BlendMode}.
+ */
+public final class BlendModeColorFilter extends ColorFilter {
+
+ @ColorInt final int mColor;
+ private final BlendMode mMode;
+
+ public BlendModeColorFilter(@ColorInt int color, @NonNull BlendMode mode) {
+ mColor = color;
+ mMode = mode;
+ }
+
+
+ /**
+ * Returns the ARGB color used to tint the source pixels when this filter
+ * is applied.
+ *
+ * @see Color
+ *
+ */
+ @ColorInt
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Returns the Porter-Duff mode used to composite this color filter's
+ * color with the source pixel when this filter is applied.
+ *
+ * @see BlendMode
+ *
+ */
+ public BlendMode getMode() {
+ return mMode;
+ }
+
+ @Override
+ long createNativeInstance() {
+ return native_CreateBlendModeFilter(mColor, mMode.getXfermode().porterDuffMode);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ final BlendModeColorFilter other = (BlendModeColorFilter) object;
+ return other.mMode == mMode;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * mMode.hashCode() + mColor;
+ }
+
+ private static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
+
+}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 135c137..6798ab2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1684,12 +1684,26 @@
*
* @param color the color to draw with
* @param mode the porter-duff mode to apply to the color
+ *
+ * @deprecated use {@link #drawColor(int, BlendMode)} instead
*/
+ @Deprecated
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
super.drawColor(color, mode);
}
/**
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
+ * blendmode.
+ *
+ * @param color the color to draw with
+ * @param mode the blendmode to apply to the color
+ */
+ public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
+ super.drawColor(color, mode);
+ }
+
+ /**
* Draw a line segment with the specified start and stop x,y coordinates, using the specified
* paint.
* <p>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 69ff3bc..6821282 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1168,12 +1168,29 @@
* Get the paint's transfer mode object.
*
* @return the paint's transfer mode (or null)
+ *
+ * @deprecated use {@link #getBlendMode()} instead
*/
+ @Deprecated
public Xfermode getXfermode() {
return mXfermode;
}
/**
+ * Get the paint's blend mode object.
+ *
+ * @return the paint's blend mode (or null)
+ */
+ @Nullable
+ public BlendMode getBlendMode() {
+ if (mXfermode == null) {
+ return null;
+ } else {
+ return BlendMode.fromValue(mXfermode.porterDuffMode);
+ }
+ }
+
+ /**
* Set or clear the transfer mode object. A transfer mode defines how
* source pixels (generate by a drawing command) are composited with
* the destination pixels (content of the render target).
@@ -1185,8 +1202,17 @@
*
* @param xfermode May be null. The xfermode to be installed in the paint
* @return xfermode
+ *
+ * @deprecated Use {@link #setBlendMode} to apply a Xfermode directly
+ * through usage of {@link BlendMode}
*/
+ @Deprecated
public Xfermode setXfermode(Xfermode xfermode) {
+ return installXfermode(xfermode);
+ }
+
+ @Nullable
+ private Xfermode installXfermode(Xfermode xfermode) {
int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
if (newMode != curMode) {
@@ -1197,6 +1223,23 @@
}
/**
+ * Set or clear the blend mode. A blend mode defines how source pixels
+ * (generated by a drawing command) are composited with the destination pixels
+ * (content of the render target).
+ * <p />
+ * Pass null to clear any previous blend mode.
+ * As a convenience, the parameter passed is also returned.
+ * <p />
+ *
+ * @see BlendMode
+ *
+ * @param blendmode May be null. The blend mode to be installed in the paint
+ */
+ public void setBlendMode(@Nullable BlendMode blendmode) {
+ installXfermode(blendmode != null ? blendmode.getXfermode() : null);
+ }
+
+ /**
* Get the paint's patheffect object.
*
* @return the paint's patheffect (or null)
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 6665220..c2a8eb7 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -23,7 +23,11 @@
/**
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link PorterDuff Porter-Duff composite mode}.
+ *
+ * @deprecated Consider using {@link BlendModeColorFilter} instead as it supports a wider
+ * set of blend modes than those defined in {@link PorterDuff.Mode}
*/
+@Deprecated
public class PorterDuffColorFilter extends ColorFilter {
@ColorInt
private int mColor;
@@ -71,7 +75,7 @@
@Override
long createNativeInstance() {
- return native_CreatePorterDuffFilter(mColor, mMode.nativeInt);
+ return native_CreateBlendModeFilter(mColor, mMode.nativeInt);
}
@Override
@@ -91,5 +95,5 @@
return 31 * mMode.hashCode() + mColor;
}
- private static native long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode);
+ private static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 5bd59d4..09d0a58 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -595,7 +595,12 @@
* <p class="note"><strong>Note:</strong> Setting a color filter disables
* {@link #setTintList(ColorStateList) tint}.
* </p>
+ *
+ * @see {@link #setColorFilter(ColorFilter)} }
+ * @deprecated use {@link #setColorFilter(ColorFilter)} with an instance
+ * of {@link android.graphics.BlendModeColorFilter}
*/
+ @Deprecated
public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
if (getColorFilter() instanceof PorterDuffColorFilter) {
PorterDuffColorFilter existing = (PorterDuffColorFilter) getColorFilter();
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 7480fa0..52e9ae1 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -25,10 +25,14 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Log;
+
import java.lang.ref.WeakReference;
-import java.nio.ByteOrder;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -225,35 +229,12 @@
* The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
* enumeration.
*/
- public static class Descriptor {
+ public static final class Descriptor implements Parcelable {
public Descriptor() {
}
/**
- * @param type UUID identifying the effect type. May be one of:
- * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
- * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
- * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
- * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
- * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
- * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
- * @param uuid UUID for this particular implementation
- * @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
- * @param name human readable effect name
- * @param implementor human readable effect implementor name
- *
- */
- public Descriptor(String type, String uuid, String connectMode,
- String name, String implementor) {
- this.type = UUID.fromString(type);
- this.uuid = UUID.fromString(uuid);
- this.connectMode = connectMode;
- this.name = name;
- this.implementor = implementor;
- }
-
- /**
* Indicates the generic type of the effect (Equalizer, Bass boost ...).
* One of {@link AudioEffect#EFFECT_TYPE_AEC},
* {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
@@ -289,7 +270,86 @@
* Human readable effect implementor name
*/
public String implementor;
- };
+
+ /**
+ * @param type UUID identifying the effect type. May be one of:
+ * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
+ * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
+ * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
+ * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
+ * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
+ * @param uuid UUID for this particular implementation
+ * @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
+ * @param name human readable effect name
+ * @param implementor human readable effect implementor name
+ *
+ */
+ public Descriptor(String type, String uuid, String connectMode,
+ String name, String implementor) {
+ this.type = UUID.fromString(type);
+ this.uuid = UUID.fromString(uuid);
+ this.connectMode = connectMode;
+ this.name = name;
+ this.implementor = implementor;
+ }
+
+ private Descriptor(Parcel in) {
+ type = UUID.fromString(in.readString());
+ uuid = UUID.fromString(in.readString());
+ connectMode = in.readString();
+ name = in.readString();
+ implementor = in.readString();
+ }
+
+ public static final Parcelable.Creator<Descriptor> CREATOR =
+ new Parcelable.Creator<Descriptor>() {
+ /**
+ * Rebuilds a Descriptor previously stored with writeToParcel().
+ * @param p Parcel object to read the Descriptor from
+ * @return a new Descriptor created from the data in the parcel
+ */
+ public Descriptor createFromParcel(Parcel p) {
+ return new Descriptor(p);
+ }
+ public Descriptor[] newArray(int size) {
+ return new Descriptor[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, uuid, connectMode, name, implementor);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(type.toString());
+ dest.writeString(uuid.toString());
+ dest.writeString(connectMode);
+ dest.writeString(name);
+ dest.writeString(implementor);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof Descriptor)) return false;
+
+ Descriptor that = (Descriptor) o;
+
+ return (type.equals(that.type)
+ && uuid.equals(that.uuid)
+ && connectMode.equals(that.connectMode)
+ && name.equals(that.name)
+ && implementor.equals(that.implementor));
+ }
+ }
/**
* Effect connection mode is insert. Specifying an audio session ID when creating the effect
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index f244f9f..74d6605 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -26,6 +26,7 @@
],
static_libs: [
+ "CarNotificationLib",
"SystemUI-core",
"SystemUIPluginLib",
"SystemUISharedLib",
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 452d61d..572737f 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -28,4 +28,31 @@
<bool name="config_enableRightNavigationBar">false</bool>
<bool name="config_enableBottomNavigationBar">true</bool>
+ <!-- SystemUI Services: The classes of the stuff to start. This is duplicated from core
+ SystemUi b/c it can't be overlayed at this level for now
+ -->
+ <string-array name="config_systemUIServiceComponents" translatable="false">
+ <item>com.android.systemui.Dependency</item>
+ <item>com.android.systemui.util.NotificationChannels</item>
+ <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
+ <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
+ <item>com.android.systemui.recents.Recents</item>
+ <item>com.android.systemui.volume.VolumeUI</item>
+ <item>com.android.systemui.stackdivider.Divider</item>
+ <item>com.android.systemui.SystemBars</item>
+ <item>com.android.systemui.usb.StorageNotification</item>
+ <item>com.android.systemui.power.PowerUI</item>
+ <item>com.android.systemui.media.RingtonePlayer</item>
+ <item>com.android.systemui.keyboard.KeyboardUI</item>
+ <item>com.android.systemui.pip.PipUI</item>
+ <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
+ <item>@string/config_systemUIVendorServiceComponent</item>
+ <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
+ <item>com.android.systemui.LatencyTester</item>
+ <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
+ <item>com.android.systemui.ScreenDecorations</item>
+ <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+ <item>com.android.systemui.SliceBroadcastRelayHandler</item>
+ <item>com.android.systemui.notifications.NotificationsUI</item>
+ </string-array>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
new file mode 100644
index 0000000..cb92c42
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notifications;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.car.notification.CarNotificationListener;
+import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationViewController;
+import com.android.car.notification.PreprocessingManager;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+/**
+ * Standalone SystemUI for displaying Notifications that have been designed to be used in the car
+ */
+public class NotificationsUI extends SystemUI {
+
+ private static final String TAG = "NotificationsUI";
+ private CarNotificationListener mCarNotificationListener;
+ private CarUxRestrictionsManager mCarUxRestrictionsManager;
+ private Car mCar;
+ private ViewGroup mCarNotificationWindow;
+ private NotificationViewController mNotificationViewController;
+ private boolean mIsShowing;
+ private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper =
+ new CarUxRestrictionManagerWrapper();
+
+ /**
+ * Inits the window that hosts the notifications and establishes the connections
+ * to the car related services.
+ */
+ @Override
+ public void start() {
+ WindowManager windowManager =
+ (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mCarNotificationListener = new CarNotificationListener();
+ mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper);
+ mCar = Car.createCar(mContext, mCarConnectionListener);
+ mCar.connect();
+
+
+ mCarNotificationWindow = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, null);
+ View.inflate(mContext,
+ com.android.car.notification.R.layout.notification_center_activity,
+ mCarNotificationWindow);
+ mCarNotificationWindow.findViewById(
+ com.android.car.notification.R.id.exit_button_container)
+ .setOnClickListener(v -> toggleShowingCarNotifications());
+
+ WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.setTitle("Car Notification Window");
+ // start in the hidden state
+ mCarNotificationWindow.setVisibility(View.GONE);
+ windowManager.addView(mCarNotificationWindow, layoutParams);
+ mNotificationViewController = new NotificationViewController(
+ mCarNotificationWindow
+ .findViewById(com.android.car.notification.R.id.notification_view),
+ PreprocessingManager.getInstance(mContext),
+ mCarNotificationListener,
+ mCarUxRestrictionManagerWrapper
+ );
+ // Add to the SystemUI component registry
+ putComponent(NotificationsUI.class, this);
+ }
+
+ /**
+ * Connection callback to establish UX Restrictions
+ */
+ private ServiceConnection mCarConnectionListener = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
+ Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper
+ .setCarUxRestrictionsManager(mCarUxRestrictionsManager);
+ PreprocessingManager preprocessingManager = PreprocessingManager.getInstance(
+ mContext);
+ preprocessingManager
+ .setCarUxRestrictionManagerWrapper(mCarUxRestrictionManagerWrapper);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car not connected in CarConnectionListener", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.e(TAG, "Car service disconnected unexpectedly");
+ }
+ };
+
+ /**
+ * Toggles the visiblity of the notifications
+ */
+ public void toggleShowingCarNotifications() {
+ if (mCarNotificationWindow.getVisibility() == View.VISIBLE) {
+ closeCarNotifications();
+ return;
+ }
+ openCarNotifications();
+ }
+
+ /**
+ * Hides the notifications
+ */
+ public void closeCarNotifications() {
+ mCarNotificationWindow.setVisibility(View.GONE);
+ mNotificationViewController.disable();
+ mIsShowing = false;
+ }
+
+ /**
+ * Sets the notifications to visible
+ */
+ public void openCarNotifications() {
+ mCarNotificationWindow.setVisibility(View.VISIBLE);
+ mNotificationViewController.enable();
+ mIsShowing = true;
+ }
+
+ /**
+ * Returns {@code true} if notifications are currently on the screen
+ */
+ public boolean isShowing() {
+ return mIsShowing;
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index 81f7846..0cba351 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -21,7 +21,6 @@
import android.view.View;
import android.widget.LinearLayout;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -34,7 +33,7 @@
*/
class CarNavigationBarView extends LinearLayout {
private View mNavButtons;
- private AlphaOptimizedImageButton mNotificationsButton;
+ private CarFacetButton mNotificationsButton;
private CarStatusBar mCarStatusBar;
private Context mContext;
private View mLockScreenButtons;
@@ -71,7 +70,7 @@
}
protected void onNotificationsClick(View v) {
- mCarStatusBar.togglePanel();
+ mCarStatusBar.toggleCarNotifications();
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 2d90f8f..5da236c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -35,6 +35,7 @@
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.notifications.NotificationsUI;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -587,4 +588,9 @@
private Drawable getDefaultWallpaper() {
return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
}
+
+ public void toggleCarNotifications() {
+ getComponent(NotificationsUI.class).toggleShowingCarNotifications();
+ }
+
}
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index ff70e97..010a810 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -63,6 +63,13 @@
android:resource="@array/autofill_field_classification_available_algorithms" />
</service>
+ <service android:name=".sms.FinancialSmsServiceImpl"
+ android:permission="android.permission.BIND_FINANCIAL_SMS_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.sms.action.FINANCIAL_SERVICE_INTENT" />
+ </intent-filter>
+ </service>
+
<library android:name="android.ext.services"/>
</application>
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
new file mode 100644
index 0000000..ab718021
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.sms;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.net.Uri;
+import android.os.Bundle;
+import android.service.sms.FinancialSmsService;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * Service to provide financial apps access to sms messages.
+ */
+public class FinancialSmsServiceImpl extends FinancialSmsService {
+
+ private static final String TAG = "FinancialSmsServiceImpl";
+ private static final String KEY_COLUMN_NAMES = "column_names";
+
+ @Nullable
+ @Override
+ public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
+ ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES);
+ if (columnNames == null || columnNames.size() <= 0) {
+ return null;
+ }
+
+ Uri inbox = Uri.parse("content://sms/inbox");
+
+ try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null);
+ CursorWindow window = new CursorWindow("FinancialSmsMessages")) {
+ int messageCount = cursor.getCount();
+ if (messageCount > 0 && cursor.moveToFirst()) {
+ window.setNumColumns(columnNames.size());
+ for (int row = 0; row < messageCount; row++) {
+ if (!window.allocRow()) {
+ Log.e(TAG, "CursorWindow ran out of memory.");
+ return null;
+ }
+ for (int col = 0; col < columnNames.size(); col++) {
+ String columnName = columnNames.get(col);
+ int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName);
+ String inboxColumnValue = cursor.getString(inboxColumnIndex);
+ boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col);
+ if (!addedToCursorWindow) {
+ Log.e(TAG, "Failed to add:"
+ + inboxColumnValue
+ + ";column:"
+ + columnName);
+ return null;
+ }
+ }
+ cursor.moveToNext();
+ }
+ } else {
+ Log.w(TAG, "No sms messages.");
+ }
+ return window;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get sms messages.");
+ return null;
+ }
+ }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
new file mode 100644
index 0000000..12575a6
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.sms;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import org.junit.Test;
+
+/**
+ * Contains the base tests for FinancialSmsServiceImpl.
+ */
+public class FinancialSmsServiceImplTest {
+
+ private final FinancialSmsServiceImpl mService = new FinancialSmsServiceImpl();
+
+ @Test
+ public void testOnGetSmsMessages_nullWithNoParamData() {
+ assertThat(mService.onGetSmsMessages(new Bundle())).isNull();
+ }
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
index 01d9c00..e27ae7d 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -73,7 +73,7 @@
<LinearLayout
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:orientation="vertical">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 0b9a7b2..294bd50 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -24,7 +24,8 @@
android:orientation="vertical"
android:paddingBottom="8dp"
android:visibility="invisible"
- android:elevation="4dp" >
+ android:elevation="4dp"
+ android:importantForAccessibility="no" >
<include
android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3851190..6037dfc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -24,7 +24,7 @@
<!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. -->
<dimen name="navigation_bar_min_swipe_distance">48dp</dimen>
<!-- The distance from a side of device of the navigation bar to start an edge swipe -->
- <dimen name="navigation_bar_edge_swipe_threshold">60dp</dimen>
+ <dimen name="navigation_bar_edge_swipe_threshold">48dp</dimen>
<!-- thickness (height) of the dead zone at the top of the navigation bar,
reducing false presses on navbar buttons; approx 2mm -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fede934..87155c4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -144,7 +144,7 @@
<item name="android:textSize">@dimen/qs_time_expanded_size</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">sans-serif</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.StatusBar.Expanded.AboveDateTime">
@@ -171,12 +171,12 @@
<style name="TextAppearance.QS">
<item name="android:textStyle">normal</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">sans-serif</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.QS.DetailHeader">
<item name="android:textSize">@dimen/qs_detail_header_text_size</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.QS.DetailItemPrimary">
@@ -202,7 +202,7 @@
<item name="android:textSize">@dimen/qs_detail_button_text_size</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:textAllCaps">true</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:gravity">center</item>
</style>
@@ -222,7 +222,7 @@
<style name="TextAppearance.QS.SegmentedButton">
<item name="android:textSize">16sp</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
</style>
<style name="TextAppearance.QS.DataUsage">
@@ -245,7 +245,7 @@
<style name="TextAppearance.QS.TileLabel.Secondary">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:fontFamily">sans-serif</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.QS.CarrierInfo">
@@ -262,7 +262,7 @@
<style name="TextAppearance.AppOpsDialog.Item">
<item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
- <item name="android:fontFamily">sans-serif</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
@@ -391,7 +391,7 @@
<style name="TextAppearance.Volume">
<item name="android:textStyle">normal</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">sans-serif</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.Volume.Header">
@@ -435,7 +435,7 @@
</style>
<style name="TextAppearance.NotificationInfo">
- <item name="android:fontFamily">sans-serif</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:textColor">@color/notification_primary_text_color</item>
</style>
@@ -463,7 +463,7 @@
</style>
<style name="TextAppearance.NotificationInfo.Button">
- <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:background">@drawable/btn_borderless_rect</item>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 9aa21f8..69efbc8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -84,7 +84,7 @@
* Processes a given touch event and updates the state.
*/
public void onTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
+ switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
if (!mAllowTouches) {
return;
@@ -92,12 +92,13 @@
// Initialize the velocity tracker
initOrResetVelocityTracker();
+ addMovement(ev);
mActivePointerId = ev.getPointerId(0);
if (DEBUG) {
Log.e(TAG, "Setting active pointer id on DOWN: " + mActivePointerId);
}
- mLastTouch.set(ev.getX(), ev.getY());
+ mLastTouch.set(ev.getRawX(), ev.getRawY());
mDownTouch.set(mLastTouch);
mAllowDraggingOffscreen = true;
mIsUserInteracting = true;
@@ -118,15 +119,15 @@
}
// Update the velocity tracker
- mVelocityTracker.addMovement(ev);
+ addMovement(ev);
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
Log.e(TAG, "Invalid active pointer id on MOVE: " + mActivePointerId);
break;
}
- float x = ev.getX(pointerIndex);
- float y = ev.getY(pointerIndex);
+ float x = ev.getRawX(pointerIndex);
+ float y = ev.getRawY(pointerIndex);
mLastDelta.set(x - mLastTouch.x, y - mLastTouch.y);
mDownDelta.set(x - mDownTouch.x, y - mDownTouch.y);
@@ -149,7 +150,7 @@
}
// Update the velocity tracker
- mVelocityTracker.addMovement(ev);
+ addMovement(ev);
int pointerIndex = ev.getActionIndex();
int pointerId = ev.getPointerId(pointerIndex);
@@ -161,7 +162,7 @@
Log.e(TAG, "Relinquish active pointer id on POINTER_UP: " +
mActivePointerId);
}
- mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex));
+ mLastTouch.set(ev.getRawX(newPointerIndex), ev.getRawY(newPointerIndex));
}
break;
}
@@ -172,7 +173,7 @@
}
// Update the velocity tracker
- mVelocityTracker.addMovement(ev);
+ addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000,
mViewConfig.getScaledMaximumFlingVelocity());
mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
@@ -184,7 +185,7 @@
}
mUpTouchTime = ev.getEventTime();
- mLastTouch.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ mLastTouch.set(ev.getRawX(pointerIndex), ev.getRawY(pointerIndex));
mPreviouslyDragging = mIsDragging;
mIsWaitingForDoubleTap = !mIsDoubleTap && !mIsDragging &&
(mUpTouchTime - mDownTouchTime) < DOUBLE_TAP_TIMEOUT;
@@ -331,6 +332,16 @@
}
}
+ private void addMovement(MotionEvent event) {
+ // Add movement to velocity tracker using raw screen X and Y coordinates instead
+ // of window coordinates because the window frame may be moving at the same time.
+ float deltaX = event.getRawX() - event.getX();
+ float deltaY = event.getRawY() - event.getY();
+ event.offsetLocation(deltaX, deltaY);
+ mVelocityTracker.addMovement(event);
+ event.offsetLocation(-deltaX, -deltaY);
+ }
+
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 32fd2dc..bfcf021 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.Path;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
@@ -88,6 +89,7 @@
float pathSize = AdaptiveIconDrawable.MASK_SIZE;
PathShape p = new PathShape(path, pathSize, pathSize);
ShapeDrawable d = new ShapeDrawable(p);
+ d.setTintList(ColorStateList.valueOf(Color.TRANSPARENT));
int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
d.setIntrinsicHeight(bgSize);
d.setIntrinsicWidth(bgSize);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 6cec36a..91b34fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -916,6 +916,10 @@
updateRelativeOffset();
}
+ public void onUiModeChanged() {
+ updateBackgroundColors();
+ }
+
private class ShelfState extends ExpandableViewState {
private float openedAmount;
private boolean hasItemsInStableShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 7876b24..1d79152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -225,7 +225,7 @@
initDimens();
}
- public void onUiModeChanged() {
+ protected void updateBackgroundColors() {
updateColors();
initBackground();
updateBackgroundTint();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8bed366..383446c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -121,6 +121,7 @@
private static final int MENU_VIEW_INDEX = 0;
private static final String TAG = "ExpandableNotifRow";
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
+ private boolean mUpdateBackgroundOnUpdate;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -587,6 +588,10 @@
updateIconVisibilities();
updateShelfIconColor();
updateRippleAllowed();
+ if (mUpdateBackgroundOnUpdate) {
+ mUpdateBackgroundOnUpdate = false;
+ updateBackgroundColors();
+ }
}
/** Called when the notification's ranking was changed (but nothing else changed). */
@@ -1212,9 +1217,8 @@
}
}
- @Override
public void onUiModeChanged() {
- super.onUiModeChanged();
+ mUpdateBackgroundOnUpdate = true;
reInflateViews();
if (mChildrenContainer != null) {
for (ExpandableNotificationRow child : mChildrenContainer.getNotificationChildren()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index eca1a14..dbe6e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -641,15 +641,7 @@
public void onUiModeChanged() {
mBgColor = mContext.getColor(R.color.notification_shade_background_color);
updateBackgroundDimming();
-
- // Re-inflate all notification views
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child instanceof ActivatableNotificationView) {
- ((ActivatableNotificationView) child).onUiModeChanged();
- }
- }
+ mShelf.onUiModeChanged();
}
@ShadeViewRefactor(RefactorComponent.DECORATOR)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a2a11bb..c7e4d34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -105,10 +105,22 @@
private static final boolean DEBUG = false;
- private static final boolean EXPAND_ON_WAKE_UP = SystemProperties.getBoolean(
+ /**
+ * If passive interrupts expand the NSSL or not
+ */
+ private static final boolean EXPAND_ON_PASSIVE_INTERRUPT = SystemProperties.getBoolean(
"persist.sysui.expand_shade_on_wake_up", true);
+ /**
+ * If the notification panel should remain collapsed when the phone wakes up, even if the user
+ * presses power.
+ */
+ private static final boolean NEVER_EXPAND_WHEN_WAKING_UP = SystemProperties.getBoolean(
+ "persist.sysui.defer_notifications_on_lock_screen", false);
+ /**
+ * If waking up the phone should take you to SHADE_LOCKED instead of KEYGUARD
+ */
private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
- "persist.sysui.go_to_shade_on_wake_up", true);
+ "persist.sysui.go_to_shade_on_wake_up", false);
/**
* Fling expanding QS.
@@ -2774,10 +2786,12 @@
}
public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
- boolean passiveInterrupted) {
+ boolean passivelyInterrupted) {
if (dozing == mDozing) return;
mDozing = dozing;
- mSemiAwake = !EXPAND_ON_WAKE_UP && !mDozing && passiveInterrupted;
+ boolean doNotExpand = (!EXPAND_ON_PASSIVE_INTERRUPT && passivelyInterrupted)
+ || NEVER_EXPAND_WHEN_WAKING_UP;
+ mSemiAwake = doNotExpand && !mDozing;
if (!mSemiAwake) {
mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index acdd5e9..261f117 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -67,6 +67,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import java.util.ArrayList;
+
public class StatusBarNotificationPresenter implements NotificationPresenter,
ConfigurationController.ConfigurationListener {
@@ -105,6 +107,7 @@
private final int mMaxAllowedKeyguardNotifications;
private final IStatusBarService mBarService;
private boolean mReinflateNotificationsOnUserSwitched;
+ private boolean mDispatchUiModeChangeOnUserSwitched;
private final UnlockMethodCache mUnlockMethodCache;
private TextView mNotificationPanelDebugText;
@@ -187,6 +190,27 @@
}
@Override
+ public void onUiModeChanged() {
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+ updateNotificationOnUiModeChanged();
+ } else {
+ mDispatchUiModeChangeOnUserSwitched = true;
+ }
+ }
+
+ private void updateNotificationOnUiModeChanged() {
+ ArrayList<Entry> userNotifications
+ = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+ for (int i = 0; i < userNotifications.size(); i++) {
+ Entry entry = userNotifications.get(i);
+ ExpandableNotificationRow row = entry.getRow();
+ if (row != null) {
+ row.onUiModeChanged();
+ }
+ }
+ }
+
+ @Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
|| mActivityLaunchAnimator.isAnimationPending()
@@ -301,6 +325,10 @@
mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
mReinflateNotificationsOnUserSwitched = false;
}
+ if (mDispatchUiModeChangeOnUserSwitched) {
+ updateNotificationOnUiModeChanged();
+ mDispatchUiModeChangeOnUserSwitched = false;
+ }
updateNotificationViews();
mMediaManager.clearCurrentMediaNotification();
mShadeController.setLockscreenUser(newUserId);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 2d30640..eb0090b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6637,6 +6637,11 @@
// OS: Q
SETTINGS_WIFI_DPP_ENROLLEE = 1596;
+ // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index eda9fe1..89194e4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1968,6 +1968,7 @@
void systemReady() {
mProxyTracker.loadGlobalProxy();
registerNetdEventCallback();
+ mTethering.systemReady();
synchronized (this) {
mSystemReady = true;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 4678fec..8869af4 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -164,8 +164,6 @@
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
/**
* Name representing {@link #setGlobalAlert(long)} limit when delivered to
* {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -959,8 +957,7 @@
public String[] listInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- final List<String> result = mNetdService.interfaceGetList();
- return result.toArray(EMPTY_STRING_ARRAY);
+ return mNetdService.interfaceGetList();
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
@@ -1252,8 +1249,7 @@
public String[] listTetheredInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- final List<String> result = mNetdService.tetherInterfaceList();
- return result.toArray(EMPTY_STRING_ARRAY);
+ return mNetdService.tetherInterfaceList();
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
@@ -1276,8 +1272,7 @@
public String[] getDnsForwarders() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- final List<String> result = mNetdService.tetherDnsList();
- return result.toArray(EMPTY_STRING_ARRAY);
+ return mNetdService.tetherDnsList();
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4b092b2..581d435 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -16,7 +16,15 @@
package com.android.server;
-import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.Manifest.permission.INSTALL_PACKAGES;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
@@ -27,9 +35,11 @@
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -51,7 +61,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ProviderInfo;
@@ -116,12 +128,14 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.os.AppFuseMount;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.FuseUnavailableMountException;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.HexDump;
@@ -204,7 +218,9 @@
@Override
public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ mStorageManagerService.servicesReady();
+ } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mStorageManagerService.systemReady();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mStorageManagerService.bootCompleted();
@@ -261,6 +277,7 @@
private static final String TAG_VOLUMES = "volumes";
private static final String ATTR_VERSION = "version";
private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
+ private static final String ATTR_ISOLATED_STORAGE = "isolatedStorage";
private static final String TAG_VOLUME = "volume";
private static final String ATTR_TYPE = "type";
private static final String ATTR_FS_UUID = "fsUuid";
@@ -311,6 +328,10 @@
@GuardedBy("mLock")
private String mPrimaryStorageUuid;
+ /** Flag indicating isolated storage state of last boot */
+ @GuardedBy("mLock")
+ private boolean mLastIsolatedStorage = false;
+
/** Map from disk ID to latches */
@GuardedBy("mLock")
private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
@@ -453,6 +474,9 @@
private UserManagerInternal mUmInternal;
private ActivityManagerInternal mAmInternal;
+ private IPackageManager mIPackageManager;
+ private IAppOpsService mIAppOpsService;
+
private final Callbacks mCallbacks;
private final LockPatternUtils mLockPatternUtils;
@@ -1565,11 +1589,83 @@
}
}
+ private void servicesReady() {
+ synchronized (mLock) {
+ final boolean thisIsolatedStorage = StorageManager.hasIsolatedStorage();
+ if (mLastIsolatedStorage == thisIsolatedStorage) {
+ // Nothing changed since last boot; keep rolling forward
+ return;
+ } else if (thisIsolatedStorage) {
+ // This boot enables isolated storage; apply legacy behavior
+ applyLegacyStorage();
+ }
+
+ // Always remember the new state we just booted with
+ writeSettingsLocked();
+ }
+ }
+
+ /**
+ * If we're enabling isolated storage, we need to remember which existing
+ * apps have already been using shared storage, and grant them legacy access
+ * to keep them running smoothly.
+ */
+ private void applyLegacyStorage() {
+ final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+ for (int userId : um.getUserIds()) {
+ final PackageManager pm;
+ try {
+ pm = mContext.createPackageContextAsUser(mContext.getPackageName(),
+ 0, UserHandle.of(userId)).getPackageManager();
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ final List<PackageInfo> pkgs = pm.getPackagesHoldingPermissions(new String[] {
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+ }, MATCH_UNINSTALLED_PACKAGES | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+ for (PackageInfo pkg : pkgs) {
+ final int uid = pkg.applicationInfo.uid;
+ final String packageName = pkg.applicationInfo.packageName;
+
+ final long lastAccess = getLastAccessTime(appOps, uid, packageName, new int[] {
+ AppOpsManager.OP_READ_EXTERNAL_STORAGE,
+ AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
+ });
+
+ Log.d(TAG, "Found " + uid + " " + packageName
+ + " with granted storage access, last accessed " + lastAccess);
+ if (lastAccess > 0) {
+ appOps.setMode(AppOpsManager.OP_LEGACY_STORAGE,
+ uid, packageName, AppOpsManager.MODE_ALLOWED);
+ }
+ }
+ }
+ }
+
+ private static long getLastAccessTime(AppOpsManager manager,
+ int uid, String packageName, int[] ops) {
+ long maxTime = 0;
+ final List<AppOpsManager.PackageOps> pkgs = manager.getOpsForPackage(uid, packageName, ops);
+ for (AppOpsManager.PackageOps pkg : CollectionUtils.defeatNullable(pkgs)) {
+ for (AppOpsManager.OpEntry op : CollectionUtils.defeatNullable(pkg.getOps())) {
+ maxTime = Math.max(maxTime, op.getLastAccessTime());
+ }
+ }
+ return maxTime;
+ }
+
private void systemReady() {
LocalServices.getService(ActivityTaskManagerInternal.class)
.registerScreenObserver(this);
mSystemReady = true;
+ mIPackageManager = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ mIAppOpsService = IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
@@ -1589,6 +1685,7 @@
private void readSettingsLocked() {
mRecords.clear();
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
+ mLastIsolatedStorage = false;
FileInputStream fis = null;
try {
@@ -1610,6 +1707,8 @@
mPrimaryStorageUuid = readStringAttribute(in,
ATTR_PRIMARY_STORAGE_UUID);
}
+ mLastIsolatedStorage = readBooleanAttribute(in,
+ ATTR_ISOLATED_STORAGE, false);
} else if (TAG_VOLUME.equals(tag)) {
final VolumeRecord rec = readVolumeRecord(in);
@@ -1640,6 +1739,7 @@
out.startTag(null, TAG_VOLUMES);
writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
+ writeBooleanAttribute(out, ATTR_ISOLATED_STORAGE, StorageManager.hasIsolatedStorage());
final int size = mRecords.size();
for (int i = 0; i < size; i++) {
final VolumeRecord rec = mRecords.valueAt(i);
@@ -2775,11 +2875,7 @@
Slog.v(TAG, "mountProxyFileDescriptor");
// We only support a narrow set of incoming mode flags
- if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
- mode = MODE_READ_WRITE;
- } else {
- mode = MODE_READ_ONLY;
- }
+ mode &= MODE_READ_WRITE;
try {
synchronized (mAppFuseLock) {
@@ -2906,7 +3002,7 @@
}
if (!foundPrimary) {
- Log.w(TAG, "No primary storage defined yet; hacking together a stub");
+ Slog.w(TAG, "No primary storage defined yet; hacking together a stub");
final boolean primaryPhysical = SystemProperties.getBoolean(
StorageManager.PROP_PRIMARY_PHYSICAL, false);
@@ -3117,7 +3213,8 @@
throw new SecurityException("Shady looking path " + path);
}
- if (!mAmInternal.isAppStorageSandboxed(pid, uid)) {
+ final int mountMode = mAmInternal.getStorageMountMode(pid, uid);
+ if (mountMode == Zygote.MOUNT_EXTERNAL_FULL) {
return path;
}
@@ -3126,6 +3223,11 @@
final String device = m.group(1);
final String devicePath = m.group(2);
+ if (mountMode == Zygote.MOUNT_EXTERNAL_INSTALLER
+ && devicePath.startsWith("Android/obb/")) {
+ return path;
+ }
+
// Does path belong to any packages belonging to this UID? If so,
// they get to go straight through to legacy paths.
final String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
@@ -3477,6 +3579,31 @@
}
}
+ private int getMountMode(int uid, String packageName) {
+ try {
+ if (Process.isIsolated(uid)) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ if (mIPackageManager.checkUidPermission(WRITE_MEDIA_STORAGE, uid)
+ == PERMISSION_GRANTED) {
+ return Zygote.MOUNT_EXTERNAL_FULL;
+ } else if (mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, uid,
+ packageName) == MODE_ALLOWED) {
+ // TODO: define a specific "legacy" mount mode
+ return Zygote.MOUNT_EXTERNAL_FULL;
+ } else if (mIPackageManager.checkUidPermission(INSTALL_PACKAGES, uid)
+ == PERMISSION_GRANTED || mIAppOpsService.checkOperation(
+ OP_REQUEST_INSTALL_PACKAGES, uid, packageName) == MODE_ALLOWED) {
+ return Zygote.MOUNT_EXTERNAL_INSTALLER;
+ } else {
+ return Zygote.MOUNT_EXTERNAL_WRITE;
+ }
+ } catch (RemoteException e) {
+ // Should not happen
+ }
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+
private static class Callbacks extends Handler {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
@@ -3718,6 +3845,9 @@
@Override
public int getExternalStorageMountMode(int uid, String packageName) {
+ if (ENABLE_ISOLATED_STORAGE) {
+ return getMountMode(uid, packageName);
+ }
// No locking - CopyOnWriteArrayList
int mountMode = Integer.MAX_VALUE;
for (ExternalStorageMountPolicy policy : mPolicies) {
@@ -3754,6 +3884,9 @@
if (uid == Process.SYSTEM_UID) {
return true;
}
+ if (ENABLE_ISOLATED_STORAGE) {
+ return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE;
+ }
// No locking - CopyOnWriteArrayList
for (ExternalStorageMountPolicy policy : mPolicies) {
final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f7acf7e..8751d24 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2019,6 +2019,13 @@
ComponentName className = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
ComponentName name = comp != null ? comp : className;
+ if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
+ name.getPackageName(), sInfo.applicationInfo.uid)) {
+ String msg = "association not allowed between packages "
+ + callingPackage + " and " + r.packageName;
+ Slog.w(TAG, "Service lookup failed: " + msg);
+ return new ServiceLookupResult(null, msg);
+ }
if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
if (isBindExternal) {
if (!sInfo.exported) {
@@ -2099,6 +2106,17 @@
}
}
if (r != null) {
+ if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName,
+ r.appInfo.uid)) {
+ String msg = "association not allowed between packages "
+ + callingPackage + " and " + r.packageName;
+ Slog.w(TAG, "Service lookup failed: " + msg);
+ return new ServiceLookupResult(null, msg);
+ }
+ if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
+ resolvedType, r.appInfo)) {
+ return new ServiceLookupResult(null, "blocked by firewall");
+ }
if (mAm.checkComponentPermission(r.permission,
callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
if (!r.exported) {
@@ -2125,11 +2143,6 @@
return null;
}
}
-
- if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
- resolvedType, r.appInfo)) {
- return null;
- }
return new ServiceLookupResult(r, null);
}
return null;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 69cfcc2..6700a53 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -658,6 +658,12 @@
ArraySet<String> mBackgroundLaunchBroadcasts;
/**
+ * When an app has restrictions on the other apps that can have associations with it,
+ * it appears here with a set of the allowed apps.
+ */
+ ArrayMap<String, ArraySet<String>> mAllowedAssociations;
+
+ /**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
@@ -2396,6 +2402,34 @@
return mBackgroundLaunchBroadcasts;
}
+ boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
+ if (mAllowedAssociations == null) {
+ mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations();
+ }
+ // Interactions with the system uid are always allowed, since that is the core system
+ // that everyone needs to be able to interact with.
+ if (UserHandle.getAppId(uid1) == SYSTEM_UID) {
+ return true;
+ }
+ if (UserHandle.getAppId(uid2) == SYSTEM_UID) {
+ return true;
+ }
+ // We won't allow this association if either pkg1 or pkg2 has a limit on the
+ // associations that are allowed with it, and the other package is not explicitly
+ // specified as one of those associations.
+ ArraySet<String> pkgs = mAllowedAssociations.get(pkg1);
+ if (pkgs != null) {
+ if (!pkgs.contains(pkg2)) {
+ return false;
+ }
+ }
+ pkgs = mAllowedAssociations.get(pkg2);
+ if (pkgs != null) {
+ return pkgs.contains(pkg1);
+ }
+ return true;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -6264,6 +6298,21 @@
return state != 'Z' && state != 'X' && state != 'x' && state != 'K';
}
+ private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
+ ProviderInfo cpi) {
+ if (callingApp == null) {
+ return validateAssociationAllowedLocked(cpi.packageName, cpi.applicationInfo.uid,
+ null, callingUid) ? null : "<null>";
+ }
+ for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
+ if (!validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i), callingApp.uid,
+ cpi.packageName, cpi.applicationInfo.uid)) {
+ return cpi.packageName;
+ }
+ }
+ return null;
+ }
+
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingTag, boolean stable,
int userId) {
@@ -6335,6 +6384,11 @@
String msg;
if (r != null && cpr.canRunHere(r)) {
+ if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+ throw new SecurityException("Content provider lookup "
+ + cpr.name.flattenToShortString()
+ + " failed: association not allowed with package " + msg);
+ }
checkTime(startTime,
"getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
@@ -6364,6 +6418,11 @@
} catch (RemoteException e) {
}
+ if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+ throw new SecurityException("Content provider lookup "
+ + cpr.name.flattenToShortString()
+ + " failed: association not allowed with package " + msg);
+ }
checkTime(startTime,
"getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
@@ -6461,6 +6520,11 @@
checkTime(startTime, "getContentProviderImpl: got app info for user");
String msg;
+ if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+ throw new SecurityException("Content provider lookup "
+ + cpr.name.flattenToShortString()
+ + " failed: association not allowed with package " + msg);
+ }
checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
!= null) {
@@ -9207,6 +9271,12 @@
pw.println("-------------------------------------------------------------------------------");
}
+ dumpAllowedAssociationsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+
+ }
mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
@@ -9474,6 +9544,14 @@
System.gc();
pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
}
+ } else if ("allowed-associations".equals(cmd)) {
+ if (opti < args.length) {
+ dumpPackage = args[opti];
+ opti++;
+ }
+ synchronized (this) {
+ dumpAllowedAssociationsLocked(fd, pw, args, opti, true, dumpPackage);
+ }
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
@@ -10824,6 +10902,44 @@
proto.end(handlerToken);
}
+ void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage) {
+ boolean needSep = false;
+ boolean printedAnything = false;
+
+ pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
+ boolean printed = false;
+ if (mAllowedAssociations != null) {
+ for (int i = 0; i < mAllowedAssociations.size(); i++) {
+ final String pkg = mAllowedAssociations.keyAt(i);
+ final ArraySet<String> asc = mAllowedAssociations.valueAt(i);
+ boolean printedHeader = false;
+ for (int j = 0; j < asc.size(); j++) {
+ if (dumpPackage == null || pkg.equals(dumpPackage)
+ || asc.valueAt(j).equals(dumpPackage)) {
+ if (!printed) {
+ pw.println(" Allowed associations (by restricted package):");
+ printed = true;
+ needSep = true;
+ printedAnything = true;
+ }
+ if (!printedHeader) {
+ pw.print(" * ");
+ pw.print(pkg);
+ pw.println(":");
+ printedHeader = true;
+ }
+ pw.print(" Allow: ");
+ pw.println(asc.valueAt(j));
+ }
+ }
+ }
+ }
+ if (!printed) {
+ pw.println(" (No association restrictions)");
+ }
+ }
+
void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -19441,16 +19557,13 @@
}
@Override
- public boolean isAppStorageSandboxed(int pid, int uid) {
- if (!StorageManager.hasIsolatedStorage()) {
- return false;
- }
+ public int getStorageMountMode(int pid, int uid) {
if (uid == SHELL_UID || uid == ROOT_UID) {
- return false;
+ return Zygote.MOUNT_EXTERNAL_FULL;
}
synchronized (mPidsSelfLocked) {
final ProcessRecord pr = mPidsSelfLocked.get(pid);
- return pr == null || pr.mountMode != Zygote.MOUNT_EXTERNAL_FULL;
+ return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 67a4d14..740c0da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -20,6 +20,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
+import static android.app.WaitResult.launchStateToString;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -491,6 +492,7 @@
final long endTime = SystemClock.uptimeMillis();
PrintWriter out = mWaitOption ? pw : getErrPrintWriter();
boolean launched = false;
+ boolean hotLaunch = false;
switch (res) {
case ActivityManager.START_SUCCESS:
launched = true;
@@ -516,6 +518,8 @@
break;
case ActivityManager.START_TASK_TO_FRONT:
launched = true;
+ //TODO(b/120981435) remove special case
+ hotLaunch = true;
out.println(
"Warning: Activity not started, its current "
+ "task has been brought to the front");
@@ -563,6 +567,9 @@
result.who = intent.getComponent();
}
pw.println("Status: " + (result.timeout ? "timeout" : "ok"));
+ final @WaitResult.LaunchState int launchState =
+ hotLaunch ? WaitResult.LAUNCH_STATE_HOT : result.launchState;
+ pw.println("LaunchState: " + launchStateToString(launchState));
if (result.who != null) {
pw.println("Activity: " + result.who.flattenToShortString());
}
@@ -2852,6 +2859,7 @@
pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
pw.println(" provider [COMP_SPEC]: provider client-side state");
pw.println(" s[ervices] [COMP_SPEC ...]: service state");
+ pw.println(" allowed-associations: current package association restrictions");
pw.println(" as[sociations]: tracked app associations");
pw.println(" lmk: stats on low memory killer");
pw.println(" lru: raw LRU process list");
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index dd3f3b5..1c1daff 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -27,7 +28,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
@@ -835,15 +835,6 @@
return;
}
- Intent intent = new Intent("android.intent.action.ANR");
- if (!mService.mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- }
- mService.broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
-
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 3a0899d..c290fbe 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -527,6 +527,24 @@
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
boolean skip = false;
+ if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+ filter.packageName, filter.owningUid)) {
+ Slog.w(TAG, "Association not allowed: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+ + filter);
+ skip = true;
+ }
+ if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, filter.receiverList.uid)) {
+ Slog.w(TAG, "Firewall blocked: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+ + filter);
+ skip = true;
+ }
if (filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
@@ -619,11 +637,6 @@
skip = true;
}
- if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, filter.receiverList.uid)) {
- skip = true;
- }
-
if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
|| filter.receiverList.app.isCrashing())) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
@@ -1082,6 +1095,24 @@
> brOptions.getMaxManifestReceiverApiLevel())) {
skip = true;
}
+ if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+ component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Association not allowed: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
+ skip = true;
+ }
+ if (!skip) {
+ skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
+ if (skip) {
+ Slog.w(TAG, "Firewall blocked: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
+ }
+ }
int perm = mService.checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
@@ -1170,10 +1201,6 @@
+ " (uid " + r.callingUid + ")");
skip = true;
}
- if (!skip) {
- skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
- }
boolean isSingleton = false;
try {
isSingleton = mService.isSingleton(info.activityInfo.processName,
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d75601b..9dfdddb 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1382,7 +1382,7 @@
return;
}
- mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
+ mUpstreamNetworkMonitor.startObserveAllNetworks();
// TODO: De-duplicate with updateUpstreamWanted() below.
if (upstreamWanted()) {
@@ -1658,6 +1658,10 @@
}
}
+ public void systemReady() {
+ mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest());
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
// Binder.java closes the resource for us.
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3e5d5aa..3ac311b 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -55,10 +55,13 @@
* A class to centralize all the network and link properties information
* pertaining to the current and any potential upstream network.
*
- * Calling #start() registers two callbacks: one to track the system default
- * network and a second to observe all networks. The latter is necessary
- * while the expression of preferred upstreams remains a list of legacy
- * connectivity types. In future, this can be revisited.
+ * The owner of UNM gets it to register network callbacks by calling the
+ * following methods :
+ * Calling #startTrackDefaultNetwork() to track the system default network.
+ * Calling #startObserveAllNetworks() to observe all networks. Listening all
+ * networks is necessary while the expression of preferred upstreams remains
+ * a list of legacy connectivity types. In future, this can be revisited.
+ * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network.
*
* The methods and data members of this class are only to be accessed and
* modified from the tethering master state machine thread. Any other
@@ -119,33 +122,31 @@
mCM = cm;
}
- public void start(NetworkRequest defaultNetworkRequest) {
- stop();
-
- final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
- .clearCapabilities().build();
- mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
- cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
-
- if (defaultNetworkRequest != null) {
- // This is not really a "request", just a way of tracking the system default network.
- // It's guaranteed not to actually bring up any networks because it's the same request
- // as the ConnectivityService default request, and thus shares fate with it. We can't
- // use registerDefaultNetworkCallback because it will not track the system default
- // network if there is a VPN that applies to our UID.
+ public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest) {
+ // This is not really a "request", just a way of tracking the system default network.
+ // It's guaranteed not to actually bring up any networks because it's the same request
+ // as the ConnectivityService default request, and thus shares fate with it. We can't
+ // use registerDefaultNetworkCallback because it will not track the system default
+ // network if there is a VPN that applies to our UID.
+ if (mDefaultNetworkCallback == null) {
final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
}
}
+ public void startObserveAllNetworks() {
+ stop();
+
+ final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
+ .clearCapabilities().build();
+ mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
+ cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
+ }
+
public void stop() {
releaseMobileNetworkRequest();
- releaseCallback(mDefaultNetworkCallback);
- mDefaultNetworkCallback = null;
- mDefaultInternetNetwork = null;
-
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
@@ -264,9 +265,7 @@
mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
}
- private void handleNetCap(int callbackType, Network network, NetworkCapabilities newNc) {
- if (callbackType == CALLBACK_DEFAULT_INTERNET) mDefaultInternetNetwork = network;
-
+ private void handleNetCap(Network network, NetworkCapabilities newNc) {
final NetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
@@ -315,31 +314,25 @@
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
}
- private void handleSuspended(int callbackType, Network network) {
- if (callbackType != CALLBACK_LISTEN_ALL) return;
+ private void handleSuspended(Network network) {
if (!network.equals(mTetheringUpstreamNetwork)) return;
mLog.log("SUSPENDED current upstream: " + network);
}
- private void handleResumed(int callbackType, Network network) {
- if (callbackType != CALLBACK_LISTEN_ALL) return;
+ private void handleResumed(Network network) {
if (!network.equals(mTetheringUpstreamNetwork)) return;
mLog.log("RESUMED current upstream: " + network);
}
- private void handleLost(int callbackType, Network network) {
- if (network.equals(mDefaultInternetNetwork)) {
- mDefaultInternetNetwork = null;
- // There are few TODOs within ConnectivityService's rematching code
- // pertaining to spurious onLost() notifications.
- //
- // TODO: simplify this, probably if favor of code that:
- // - selects a new upstream if mTetheringUpstreamNetwork has
- // been lost (by any callback)
- // - deletes the entry from the map only when the LISTEN_ALL
- // callback gets notified.
- if (callbackType == CALLBACK_DEFAULT_INTERNET) return;
- }
+ private void handleLost(Network network) {
+ // There are few TODOs within ConnectivityService's rematching code
+ // pertaining to spurious onLost() notifications.
+ //
+ // TODO: simplify this, probably if favor of code that:
+ // - selects a new upstream if mTetheringUpstreamNetwork has
+ // been lost (by any callback)
+ // - deletes the entry from the map only when the LISTEN_ALL
+ // callback gets notified.
if (!mNetworkMap.containsKey(network)) {
// Ignore loss of networks about which we had not previously
@@ -393,11 +386,17 @@
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- handleNetCap(mCallbackType, network, newNc);
+ if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
+ mDefaultInternetNetwork = network;
+ return;
+ }
+ handleNetCap(network, newNc);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
+ if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return;
+
handleLinkProp(network, newLp);
// Any non-LISTEN_ALL callback will necessarily concern a network that will
// also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
@@ -409,17 +408,25 @@
@Override
public void onNetworkSuspended(Network network) {
- handleSuspended(mCallbackType, network);
+ if (mCallbackType == CALLBACK_LISTEN_ALL) {
+ handleSuspended(network);
+ }
}
@Override
public void onNetworkResumed(Network network) {
- handleResumed(mCallbackType, network);
+ if (mCallbackType == CALLBACK_LISTEN_ALL) {
+ handleResumed(network);
+ }
}
@Override
public void onLost(Network network) {
- handleLost(mCallbackType, network);
+ if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
+ mDefaultInternetNetwork = null;
+ return;
+ }
+ handleLost(network);
// Any non-LISTEN_ALL callback will necessarily concern a network that will
// also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
// So it's not useful to do this work for non-LISTEN_ALL callbacks.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 78e18e9..1325f04 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -861,6 +861,11 @@
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
+ synchronized (mLock) {
+ for (int c = 0; c < mControllers.size(); ++c) {
+ mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
+ }
+ }
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -868,6 +873,11 @@
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
+ synchronized (mLock) {
+ for (int c = 0; c < mControllers.size(); ++c) {
+ mControllers.get(c).onUserRemovedLocked(userId);
+ }
+ }
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 8f104e4..aca02bf 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -296,6 +296,12 @@
mRequestedWhitelistJobs.remove(uid);
}
+ @GuardedBy("mLock")
+ @Override
+ public void onAppRemovedLocked(String pkgName, int uid) {
+ mTrackedJobs.delete(uid);
+ }
+
/**
* Test to see if running the given job on the given network is insane.
* <p>
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 660c238..58ee217 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -98,6 +98,19 @@
data.put(packageName, obj);
}
+ /** Removes all the data for the user, if there was any. */
+ public void delete(int userId) {
+ mData.delete(userId);
+ }
+
+ /** Removes the data for the user and package, if there was any. */
+ public void delete(int userId, @NonNull String packageName) {
+ ArrayMap<String, T> data = mData.get(userId);
+ if (data != null) {
+ data.remove(packageName);
+ }
+ }
+
@Nullable
public T get(int userId, @NonNull String packageName) {
ArrayMap<String, T> data = mData.get(userId);
@@ -371,6 +384,38 @@
}
}
+ @Override
+ public void onAppRemovedLocked(String packageName, int uid) {
+ if (packageName == null) {
+ Slog.wtf(TAG, "Told app removed but given null package name.");
+ return;
+ }
+ final int userId = UserHandle.getUserId(uid);
+ mTrackedJobs.delete(userId, packageName);
+ Timer timer = mPkgTimers.get(userId, packageName);
+ if (timer != null) {
+ if (timer.isActive()) {
+ Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off.");
+ timer.dropEverything();
+ }
+ mPkgTimers.delete(userId, packageName);
+ }
+ mTimingSessions.delete(userId, packageName);
+ QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+ if (alarmListener != null) {
+ mAlarmManager.cancel(alarmListener);
+ mInQuotaAlarmListeners.delete(userId, packageName);
+ }
+ }
+
+ @Override
+ public void onUserRemovedLocked(int userId) {
+ mTrackedJobs.delete(userId);
+ mPkgTimers.delete(userId);
+ mTimingSessions.delete(userId);
+ mInQuotaAlarmListeners.delete(userId);
+ }
+
/**
* Returns an appropriate standby bucket for the job, taking into account any standby
* exemptions.
@@ -882,6 +927,15 @@
}
}
+ /**
+ * Stops tracking all jobs and cancels any pending alarms. This should only be called if
+ * the Timer is not going to be used anymore.
+ */
+ void dropEverything() {
+ mRunningBgJobs.clear();
+ cancelCutoff();
+ }
+
private void emitSessionLocked(long nowElapsed) {
if (mBgJobCount <= 0) {
// Nothing to emit.
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 61dc479..97c3bac 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -83,6 +83,28 @@
public void onConstantsUpdatedLocked() {
}
+ /** Called when a package is uninstalled from the device (not for an update). */
+ public void onAppRemovedLocked(String packageName, int uid) {
+ }
+
+ /** Called when a user is removed from the device. */
+ public void onUserRemovedLocked(int userId) {
+ }
+
+ /**
+ * Called when JobSchedulerService has determined that the job is not ready to be run. The
+ * Controller can evaluate if it can or should do something to promote this job's readiness.
+ */
+ public void evaluateStateLocked(JobStatus jobStatus) {
+ }
+
+ /**
+ * Called when something with the UID has changed. The controller should re-evaluate any
+ * internal state tracking dependent on this UID.
+ */
+ public void reevaluateStateLocked(int uid) {
+ }
+
protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
// This is very cheap to check (just a few conditions on data in JobStatus).
final boolean jobWouldBeReady = jobStatus.wouldBeReadyWithConstraint(constraint);
@@ -100,20 +122,6 @@
return mService.areComponentsInPlaceLocked(jobStatus);
}
- /**
- * Called when JobSchedulerService has determined that the job is not ready to be run. The
- * Controller can evaluate if it can or should do something to promote this job's readiness.
- */
- public void evaluateStateLocked(JobStatus jobStatus) {
- }
-
- /**
- * Called when something with the UID has changed. The controller should re-evaluate any
- * internal state tracking dependent on this UID.
- */
- public void reevaluateStateLocked(int uid) {
- }
-
public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate);
public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc364c0..36a027a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -583,7 +583,6 @@
private void loadPolicyFile() {
if (DBG) Slog.d(TAG, "loadPolicyFile");
synchronized (mPolicyFile) {
-
InputStream infile = null;
try {
infile = mPolicyFile.openRead();
@@ -3357,14 +3356,12 @@
Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
return null;
}
- synchronized(mPolicyFile) {
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- writePolicyXml(baos, true /*forBackup*/);
- return baos.toByteArray();
- } catch (IOException e) {
- Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
- }
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ writePolicyXml(baos, true /*forBackup*/);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
}
return null;
}
@@ -3383,14 +3380,12 @@
Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
return;
}
- synchronized(mPolicyFile) {
- final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
- try {
- readPolicyXml(bais, true /*forRestore*/);
- handleSavePolicyFile();
- } catch (NumberFormatException | XmlPullParserException | IOException e) {
- Slog.w(TAG, "applyRestore: error reading payload", e);
- }
+ final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+ try {
+ readPolicyXml(bais, true /*forRestore*/);
+ handleSavePolicyFile();
+ } catch (NumberFormatException | XmlPullParserException | IOException e) {
+ Slog.w(TAG, "applyRestore: error reading payload", e);
}
}
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
new file mode 100644
index 0000000..2ae424d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.server.pm.dex.DexLogger;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Scheduled job to trigger logging of app dynamic code loading. This runs daily while idle and
+ * charging. The actual logging is performed by {@link DexLogger}.
+ * {@hide}
+ */
+public class DynamicCodeLoggingService extends JobService {
+ private static final String TAG = DynamicCodeLoggingService.class.getName();
+
+ private static final int JOB_ID = 2030028;
+ private static final long PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+ private volatile boolean mStopRequested = false;
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Schedule our job with the {@link JobScheduler}.
+ */
+ public static void schedule(Context context) {
+ ComponentName serviceName = new ComponentName(
+ "android", DynamicCodeLoggingService.class.getName());
+
+ JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ js.schedule(new JobInfo.Builder(JOB_ID, serviceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .setPeriodic(PERIOD_MILLIS)
+ .build());
+ if (DEBUG) {
+ Log.d(TAG, "Job scheduled");
+ }
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ if (DEBUG) {
+ Log.d(TAG, "onStartJob");
+ }
+ mStopRequested = false;
+ new IdleLoggingThread(params).start();
+ return true; // Job is running on another thread
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ if (DEBUG) {
+ Log.d(TAG, "onStopJob");
+ }
+ mStopRequested = true;
+ return true; // Requests job be re-scheduled.
+ }
+
+ private class IdleLoggingThread extends Thread {
+ private final JobParameters mParams;
+
+ IdleLoggingThread(JobParameters params) {
+ super("DynamicCodeLoggingService_IdleLoggingJob");
+ mParams = params;
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "Starting logging run");
+ }
+
+ PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
+ DexLogger dexLogger = pm.getDexManager().getDexLogger();
+ for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) {
+ if (mStopRequested) {
+ Log.w(TAG, "Stopping logging run at scheduler request");
+ return;
+ }
+
+ dexLogger.logDynamicCodeLoading(packageName);
+ }
+
+ jobFinished(mParams, /* reschedule */ false);
+ if (DEBUG) {
+ Log.d(TAG, "Finished logging run");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e9100e..6cfb846 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
@@ -304,7 +303,6 @@
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
-import com.android.server.pm.dex.DexLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
@@ -2168,10 +2166,7 @@
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
- DexManager.Listener dexManagerListener = DexLogger.getListener(this,
- installer, mInstallLock);
- mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock,
- dexManagerListener);
+ mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
@@ -9215,7 +9210,7 @@
/**
* Reconcile the information we have about the secondary dex files belonging to
- * {@code packagName} and the actual dex files. For all dex files that were
+ * {@code packageName} and the actual dex files. For all dex files that were
* deleted, update the internal records and delete the generated oat files.
*/
@Override
@@ -20185,11 +20180,6 @@
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (StorageManager.hasIsolatedStorage()) {
- return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
- ? Zygote.MOUNT_EXTERNAL_FULL
- : Zygote.MOUNT_EXTERNAL_WRITE;
- }
if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_DEFAULT;
}
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
index 88d9e52..68a755b 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -18,29 +18,32 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.os.FileUtils;
import android.os.RemoteException;
-
-import android.util.ArraySet;
+import android.os.storage.StorageManager;
import android.util.ByteStringUtils;
import android.util.EventLog;
import android.util.PackageUtils;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
import java.io.File;
+import java.util.Map;
import java.util.Set;
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
-
/**
* This class is responsible for logging data about secondary dex files.
* The data logged includes hashes of the name and content of each file.
*/
-public class DexLogger implements DexManager.Listener {
+public class DexLogger {
private static final String TAG = "DexLogger";
// Event log tag & subtag used for SafetyNet logging of dynamic
@@ -49,75 +52,172 @@
private static final String DCL_SUBTAG = "dcl";
private final IPackageManager mPackageManager;
+ private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
- public static DexManager.Listener getListener(IPackageManager pms,
- Installer installer, Object installLock) {
- return new DexLogger(pms, installer, installLock);
+ public DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+ this(pms, installer, installLock, new PackageDynamicCodeLoading());
}
@VisibleForTesting
- /*package*/ DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+ DexLogger(IPackageManager pms, Installer installer, Object installLock,
+ PackageDynamicCodeLoading packageDynamicCodeLoading) {
mPackageManager = pms;
+ mPackageDynamicCodeLoading = packageDynamicCodeLoading;
mInstaller = installer;
mInstallLock = installLock;
}
- /**
- * Compute and log hashes of the name and content of a secondary dex file.
- */
- @Override
- public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
- String dexPath, int storageFlags) {
- int ownerUid = appInfo.uid;
+ public Set<String> getAllPackagesWithDynamicCodeLoading() {
+ return mPackageDynamicCodeLoading.getAllPackagesWithDynamicCodeLoading();
+ }
- byte[] hash = null;
- synchronized(mInstallLock) {
- try {
- hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
- ownerUid, appInfo.volumeUuid, storageFlags);
- } catch (InstallerException e) {
- Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
- " : " + e.getMessage());
- }
- }
- if (hash == null) {
+ /**
+ * Write information about code dynamically loaded by {@code packageName} to the event log.
+ */
+ public void logDynamicCodeLoading(String packageName) {
+ PackageDynamicCode info = getPackageDynamicCodeInfo(packageName);
+ if (info == null) {
return;
}
- String dexFileName = new File(dexPath).getName();
- String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
- // Valid SHA256 will be 256 bits, 32 bytes.
- if (hash.length == 32) {
- message = message + ' ' + ByteStringUtils.toHexString(hash);
- }
+ SparseArray<ApplicationInfo> appInfoByUser = new SparseArray<>();
+ boolean needWrite = false;
- writeDclEvent(ownerUid, message);
+ for (Map.Entry<String, DynamicCodeFile> fileEntry : info.mFileUsageMap.entrySet()) {
+ String filePath = fileEntry.getKey();
+ DynamicCodeFile fileInfo = fileEntry.getValue();
+ int userId = fileInfo.mUserId;
- if (dexUseInfo.isUsedByOtherApps()) {
- Set<String> otherPackages = dexUseInfo.getLoadingPackages();
- Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
- for (String otherPackageName : otherPackages) {
+ int index = appInfoByUser.indexOfKey(userId);
+ ApplicationInfo appInfo;
+ if (index >= 0) {
+ appInfo = appInfoByUser.get(userId);
+ } else {
+ appInfo = null;
+
try {
- int otherUid = mPackageManager.getPackageUid(
- otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
- if (otherUid != -1 && otherUid != ownerUid) {
- otherUids.add(otherUid);
- }
- } catch (RemoteException ignore) {
+ PackageInfo ownerInfo =
+ mPackageManager.getPackageInfo(packageName, /*flags*/ 0, userId);
+ appInfo = ownerInfo == null ? null : ownerInfo.applicationInfo;
+ } catch (RemoteException ignored) {
// Can't happen, we're local.
}
+ appInfoByUser.put(userId, appInfo);
+ if (appInfo == null) {
+ Slog.d(TAG, "Could not find package " + packageName + " for user " + userId);
+ // Package has probably been uninstalled for user.
+ needWrite |= mPackageDynamicCodeLoading.removeUserPackage(packageName, userId);
+ }
}
- for (int otherUid : otherUids) {
- writeDclEvent(otherUid, message);
+
+ if (appInfo == null) {
+ continue;
}
+
+ int storageFlags;
+ if (appInfo.deviceProtectedDataDir != null
+ && FileUtils.contains(appInfo.deviceProtectedDataDir, filePath)) {
+ storageFlags = StorageManager.FLAG_STORAGE_DE;
+ } else if (appInfo.credentialProtectedDataDir != null
+ && FileUtils.contains(appInfo.credentialProtectedDataDir, filePath)) {
+ storageFlags = StorageManager.FLAG_STORAGE_CE;
+ } else {
+ Slog.e(TAG, "Could not infer CE/DE storage for path " + filePath);
+ needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
+ continue;
+ }
+
+ byte[] hash = null;
+ synchronized (mInstallLock) {
+ try {
+ hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
+ appInfo.volumeUuid, storageFlags);
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Got InstallerException when hashing file " + filePath
+ + ": " + e.getMessage());
+ }
+ }
+
+ String fileName = new File(filePath).getName();
+ String message = PackageUtils.computeSha256Digest(fileName.getBytes());
+
+ // Valid SHA256 will be 256 bits, 32 bytes.
+ if (hash != null && hash.length == 32) {
+ message = message + ' ' + ByteStringUtils.toHexString(hash);
+ } else {
+ Slog.d(TAG, "Got no hash for " + filePath);
+ // File has probably been deleted.
+ needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
+ }
+
+ for (String loadingPackageName : fileInfo.mLoadingPackages) {
+ int loadingUid = -1;
+ if (loadingPackageName.equals(packageName)) {
+ loadingUid = appInfo.uid;
+ } else {
+ try {
+ loadingUid = mPackageManager.getPackageUid(loadingPackageName, /*flags*/ 0,
+ userId);
+ } catch (RemoteException ignored) {
+ // Can't happen, we're local.
+ }
+ }
+
+ if (loadingUid != -1) {
+ writeDclEvent(loadingUid, message);
+ }
+ }
+ }
+
+ if (needWrite) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
}
}
@VisibleForTesting
- /*package*/ void writeDclEvent(int uid, String message) {
+ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+ return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
+ }
+
+ @VisibleForTesting
+ void writeDclEvent(int uid, String message) {
EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, uid, message);
}
+
+ void record(int loaderUserId, String dexPath,
+ String owningPackageName, String loadingPackageName) {
+ if (mPackageDynamicCodeLoading.record(owningPackageName, dexPath,
+ PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
+ loadingPackageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+ }
+
+ void clear() {
+ mPackageDynamicCodeLoading.clear();
+ }
+
+ void removePackage(String packageName) {
+ if (mPackageDynamicCodeLoading.removePackage(packageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+ }
+
+ void removeUserPackage(String packageName, int userId) {
+ if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+ }
+
+ void readAndSync(Map<String, Set<Integer>> packageToUsersMap) {
+ mPackageDynamicCodeLoading.read();
+ mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+ }
+
+ void writeNow() {
+ mPackageDynamicCodeLoading.writeNow();
+ }
}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 36b7269..25ef767 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -19,7 +19,6 @@
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
-import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
import android.content.ContentResolver;
import android.content.Context;
@@ -90,18 +89,17 @@
// encode and save the dex usage data.
private final PackageDexUsage mPackageDexUsage;
- // PackageDynamicCodeLoading handles recording of dynamic code loading -
- // which is similar to PackageDexUsage but records a different aspect of the data.
+ // DexLogger handles recording of dynamic code loading - which is similar to PackageDexUsage
+ // but records a different aspect of the data.
// (It additionally includes DEX files loaded with unsupported class loaders, and doesn't
// record class loaders or ISAs.)
- private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+ private final DexLogger mDexLogger;
private final IPackageManager mPackageManager;
private final PackageDexOptimizer mPackageDexOptimizer;
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
- private final Listener mListener;
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
@@ -122,26 +120,20 @@
*/
private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
- public interface Listener {
- /**
- * Invoked just before the secondary dex file {@code dexPath} for the specified application
- * is reconciled.
- */
- void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
- String dexPath, int storageFlags);
- }
-
public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo,
- Installer installer, Object installLock, Listener listener) {
+ Installer installer, Object installLock) {
mContext = context;
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
- mPackageDynamicCodeLoading = new PackageDynamicCodeLoading();
mPackageManager = pms;
mPackageDexOptimizer = pdo;
mInstaller = installer;
mInstallLock = installLock;
- mListener = listener;
+ mDexLogger = new DexLogger(pms, installer, installLock);
+ }
+
+ public DexLogger getDexLogger() {
+ return mDexLogger;
}
public void systemReady() {
@@ -243,11 +235,8 @@
continue;
}
- if (mPackageDynamicCodeLoading.record(searchResult.mOwningPackageName, dexPath,
- PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
- loadingAppInfo.packageName)) {
- mPackageDynamicCodeLoading.maybeWriteAsync();
- }
+ mDexLogger.record(loaderUserId, dexPath, searchResult.mOwningPackageName,
+ loadingAppInfo.packageName);
if (classLoaderContexts != null) {
@@ -284,7 +273,7 @@
loadInternal(existingPackages);
} catch (Exception e) {
mPackageDexUsage.clear();
- mPackageDynamicCodeLoading.clear();
+ mDexLogger.clear();
Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
}
}
@@ -335,16 +324,12 @@
if (mPackageDexUsage.removePackage(packageName)) {
mPackageDexUsage.maybeWriteAsync();
}
- if (mPackageDynamicCodeLoading.removePackage(packageName)) {
- mPackageDynamicCodeLoading.maybeWriteAsync();
- }
+ mDexLogger.removePackage(packageName);
} else {
if (mPackageDexUsage.removeUserPackage(packageName, userId)) {
mPackageDexUsage.maybeWriteAsync();
}
- if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
- mPackageDynamicCodeLoading.maybeWriteAsync();
- }
+ mDexLogger.removeUserPackage(packageName, userId);
}
}
@@ -423,10 +408,9 @@
}
try {
- mPackageDynamicCodeLoading.read();
- mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+ mDexLogger.readAndSync(packageToUsersMap);
} catch (Exception e) {
- mPackageDynamicCodeLoading.clear();
+ mDexLogger.clear();
Slog.w(TAG, "Exception while loading package dynamic code usage. "
+ "Starting with a fresh state.", e);
}
@@ -460,11 +444,6 @@
return mPackageDexUsage.getPackageUseInfo(packageName) != null;
}
- @VisibleForTesting
- /*package*/ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
- return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
- }
-
/**
* Perform dexopt on with the given {@code options} on the secondary dex files.
* @return true if all secondary dex files were processed successfully (compiled or skipped
@@ -574,10 +553,6 @@
continue;
}
- if (mListener != null) {
- mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
- }
-
boolean dexStillExists = true;
synchronized(mInstallLock) {
try {
@@ -721,7 +696,7 @@
*/
public void writePackageDexUsageNow() {
mPackageDexUsage.writeNow();
- mPackageDynamicCodeLoading.writeNow();
+ mDexLogger.writeNow();
}
private void registerSettingObserver() {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
index f74aa1d..6d4bc82 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -62,6 +62,13 @@
private static final String PACKAGE_SEPARATOR = ",";
/**
+ * Limit on how many files we store for a single owner, to avoid one app causing
+ * unbounded memory consumption.
+ */
+ @VisibleForTesting
+ static final int MAX_FILES_PER_OWNER = 100;
+
+ /**
* Regular expression to match the expected format of an input line describing one file.
* <p>Example: {@code D:10:package.name1,package.name2:/escaped/path}
* <p>The capturing groups are the file type, user ID, loading packages and escaped file path
@@ -515,6 +522,9 @@
private boolean add(String path, char fileType, int userId, String loadingPackage) {
DynamicCodeFile fileInfo = mFileUsageMap.get(path);
if (fileInfo == null) {
+ if (mFileUsageMap.size() >= MAX_FILES_PER_OWNER) {
+ return false;
+ }
fileInfo = new DynamicCodeFile(fileType, userId, loadingPackage);
mFileUsageMap.put(path, fileInfo);
return true;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 31f5ce4..b58c8116 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -799,10 +799,6 @@
continue;
}
- if (bp.isRemoved()) {
- continue;
- }
-
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
@@ -951,7 +947,8 @@
// how to disable the API to simulate revocation as legacy
// apps don't expect to run with revoked permissions.
if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
- if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && !bp.isRemoved()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
// We changed the flags, hence have to write.
updatedUserIds = ArrayUtils.appendInt(
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 3291a45..ced5935 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -19,12 +19,16 @@
import android.Manifest;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DevicePolicyManager;
import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
+import android.app.UserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -35,7 +39,9 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.DeadObjectException;
@@ -50,6 +56,7 @@
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
@@ -58,11 +65,13 @@
import android.util.Xml;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -106,8 +115,11 @@
private static final int MSG_STOP_USER = 12;
private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
+ private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
+ private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
+ private static final long TRUST_TIMEOUT_IN_MILLIS = 20 * 1000; //4 * 60 * 60 * 1000;
private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
@@ -132,6 +144,11 @@
@GuardedBy("mUsersUnlockedByBiometric")
private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
+ private final ArrayMap<Integer, TrustTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser =
+ new ArrayMap<>();
+ private AlarmManager mAlarmManager;
+ private final SettingsObserver mSettingsObserver;
+
private final StrongAuthTracker mStrongAuthTracker;
private boolean mTrustAgentsCanRun = false;
@@ -144,6 +161,8 @@
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mLockPatternUtils = new LockPatternUtils(context);
mStrongAuthTracker = new StrongAuthTracker(context);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mSettingsObserver = new SettingsObserver(mHandler);
}
@Override
@@ -170,7 +189,130 @@
}
}
- // Agent management
+ // Extend unlock config and logic
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri TRUST_AGENTS_EXTEND_UNLOCK =
+ Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK);
+
+ private final Uri LOCK_SCREEN_WHEN_TRUST_LOST =
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST);
+
+ private final ContentResolver mContentResolver;
+ private boolean mTrustAgentsExtendUnlock;
+ private boolean mLockWhenTrustLost;
+
+ /**
+ * Creates a settings observer
+ *
+ * @param handler The handler to run {@link #onChange} on, or null if none.
+ */
+ SettingsObserver(Handler handler) {
+ super(handler);
+ mContentResolver = getContext().getContentResolver();
+ updateContentObserver();
+ }
+
+ void updateContentObserver() {
+ mContentResolver.unregisterContentObserver(this);
+ mContentResolver.registerContentObserver(TRUST_AGENTS_EXTEND_UNLOCK,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ mCurrentUser);
+ mContentResolver.registerContentObserver(LOCK_SCREEN_WHEN_TRUST_LOST,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ mCurrentUser);
+
+ // Update the value immediately
+ onChange(true /* selfChange */, TRUST_AGENTS_EXTEND_UNLOCK);
+ onChange(true /* selfChange */, LOCK_SCREEN_WHEN_TRUST_LOST);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
+ mTrustAgentsExtendUnlock =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
+ 0 /* default */,
+ mCurrentUser) != 0;
+ } else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) {
+ mLockWhenTrustLost =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
+ 0 /* default */,
+ mCurrentUser) != 0;
+ }
+ }
+
+ boolean getTrustAgentsExtendUnlock() {
+ return mTrustAgentsExtendUnlock;
+ }
+
+ boolean getLockWhenTrustLost() {
+ return mLockWhenTrustLost;
+ }
+ }
+
+ private void maybeLockScreen(int userId) {
+ if (userId != mCurrentUser) {
+ return;
+ }
+
+ if (mSettingsObserver.getLockWhenTrustLost()) {
+ if (DEBUG) Slog.d(TAG, "Locking device because trust was lost");
+ try {
+ WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error locking screen when trust was lost");
+ }
+
+ // If active unlocking is not allowed, cancel any pending trust timeouts because the
+ // screen is already locked.
+ TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+ if (alarm != null && mSettingsObserver.getTrustAgentsExtendUnlock()) {
+ mAlarmManager.cancel(alarm);
+ alarm.setQueued(false /* isQueued */);
+ }
+ }
+ }
+
+ private void scheduleTrustTimeout(int userId, boolean override) {
+ int shouldOverride = override ? 1 : 0;
+ if (override) {
+ shouldOverride = 1;
+ }
+ mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, userId, shouldOverride).sendToTarget();
+ }
+
+ private void handleScheduleTrustTimeout(int userId, int shouldOverride) {
+ long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS;
+ userId = mCurrentUser;
+ TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+
+ // Cancel existing trust timeouts for this user if needed.
+ if (alarm != null) {
+ if (shouldOverride == 0 && alarm.isQueued()) {
+ if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping.");
+ return;
+ }
+ mAlarmManager.cancel(alarm);
+ } else {
+ alarm = new TrustTimeoutAlarmListener(userId);
+ mTrustTimeoutAlarmListenerForUser.put(userId, alarm);
+ }
+
+ if (DEBUG) Slog.d(TAG, "\tSetting up trust timeout alarm");
+ alarm.setQueued(true /* isQueued */);
+ mAlarmManager.setExact(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
+ mHandler);
+ }
+
+ // Agent management
private static final class AgentInfo {
CharSequence label;
@@ -202,14 +344,36 @@
}
}
+
public void updateTrust(int userId, int flags) {
+ updateTrust(userId, flags, false /* isFromUnlock */);
+ }
+
+ private void updateTrust(int userId, int flags, boolean isFromUnlock) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
&& isTrustUsuallyManagedInternal(userId) != managed) {
updateTrustUsuallyManaged(userId, managed);
}
+
boolean trusted = aggregateIsTrusted(userId);
+ IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ boolean showingKeyguard = true;
+ try {
+ showingKeyguard = wm.isKeyguardLocked();
+ } catch (RemoteException e) {
+ }
+
+ if (mSettingsObserver.getTrustAgentsExtendUnlock()) {
+ trusted = trusted && (!showingKeyguard || isFromUnlock) && userId == mCurrentUser;
+ if (DEBUG) {
+ Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted)
+ + " && " + Boolean.toString(!showingKeyguard)
+ + " && " + Boolean.toString(userId == mCurrentUser));
+ }
+ }
+
boolean changed;
synchronized (mUserIsTrusted) {
changed = mUserIsTrusted.get(userId) != trusted;
@@ -218,6 +382,11 @@
dispatchOnTrustChanged(trusted, userId, flags);
if (changed) {
refreshDeviceLockedForUser(userId);
+ if (!trusted) {
+ maybeLockScreen(userId);
+ } else {
+ scheduleTrustTimeout(userId, false /* override */);
+ }
}
}
@@ -704,6 +873,8 @@
private void dispatchUnlockAttempt(boolean successful, int userId) {
if (successful) {
mStrongAuthTracker.allowTrustFromUnlock(userId);
+ // Allow the presence of trust on a successful unlock attempt to extend unlock.
+ updateTrust(userId, 0 /* flags */, true);
}
for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -1033,8 +1204,11 @@
synchronized(mUsersUnlockedByBiometric) {
mUsersUnlockedByBiometric.put(userId, true);
}
+ // In extend unlock mode we need to refresh trust state here, which will call
+ // refreshDeviceLockedForUser()
+ int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsExtendUnlock() ? 1 : 0;
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
- 0 /* arg2 */).sendToTarget();
+ updateTrustOnUnlock).sendToTarget();
}
@Override
@@ -1114,6 +1288,7 @@
break;
case MSG_SWITCH_USER:
mCurrentUser = msg.arg1;
+ mSettingsObserver.updateContentObserver();
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_STOP_USER:
@@ -1134,8 +1309,14 @@
}
break;
case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
+ if (msg.arg2 == 1) {
+ updateTrust(msg.arg1, 0 /* flags */, true);
+ }
refreshDeviceLockedForUser(msg.arg1);
break;
+ case MSG_SCHEDULE_TRUST_TIMEOUT:
+ handleScheduleTrustTimeout(msg.arg1, msg.arg2);
+ break;
}
}
};
@@ -1245,6 +1426,15 @@
+ " agentsCanRun=" + canAgentsRunForUser(userId));
}
+ // Cancel pending alarms if we require some auth anyway.
+ if (!isTrustAllowedForUser(userId)) {
+ TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+ if (alarm != null && alarm.isQueued()) {
+ alarm.setQueued(false /* isQueued */);
+ mAlarmManager.cancel(alarm);
+ }
+ }
+
refreshAgentList(userId);
// The list of active trust agents may not have changed, if there was a previous call
@@ -1283,4 +1473,35 @@
}
}
}
+
+ private class TrustTimeoutAlarmListener implements OnAlarmListener {
+ private final int mUserId;
+ private boolean mIsQueued = false;
+
+ TrustTimeoutAlarmListener(int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void onAlarm() {
+ mIsQueued = false;
+ int strongAuthState = mStrongAuthTracker.getStrongAuthForUser(mUserId);
+
+ // Only fire if trust can unlock.
+ if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
+ if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
+ mLockPatternUtils.requireStrongAuth(
+ mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId);
+ }
+ maybeLockScreen(mUserId);
+ }
+
+ public void setQueued(boolean isQueued) {
+ mIsQueued = isQueued;
+ }
+
+ public boolean isQueued() {
+ return mIsQueued;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 12690a9..10231826 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -3,6 +3,9 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.processStateAmToProto;
+import static android.app.WaitResult.LAUNCH_STATE_COLD;
+import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WaitResult.LAUNCH_STATE_WARM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -80,6 +83,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
+import android.app.WaitResult;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.Intent;
@@ -101,10 +105,10 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
/**
@@ -259,6 +263,19 @@
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
}
+
+ @WaitResult.LaunchState int getLaunchState() {
+ switch (type) {
+ case TYPE_TRANSITION_WARM_LAUNCH:
+ return LAUNCH_STATE_WARM;
+ case TYPE_TRANSITION_HOT_LAUNCH:
+ return LAUNCH_STATE_HOT;
+ case TYPE_TRANSITION_COLD_LAUNCH:
+ return LAUNCH_STATE_COLD;
+ default:
+ return -1;
+ }
+ }
}
ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 608e450..4373675 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -145,6 +145,7 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.WaitResult.LaunchState;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -2016,10 +2017,7 @@
stopped = false;
if (isActivityTypeHome()) {
- WindowProcessController app = task.mActivities.get(0).app;
- if (hasProcess() && app != mAtmService.mHomeProcess) {
- mAtmService.mHomeProcess = app;
- }
+ mStackSupervisor.updateHomeProcess(task.mActivities.get(0).app);
}
if (nowVisible) {
@@ -2183,7 +2181,7 @@
.getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
if (info != null) {
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- info.windowsFullyDrawnDelayMs);
+ info.windowsFullyDrawnDelayMs, info.getLaunchState());
}
}
@@ -2207,8 +2205,9 @@
final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+ final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- windowsDrawnDelayMs);
+ windowsDrawnDelayMs, launchState);
mStackSupervisor.sendWaitingVisibleReportLocked(this);
finishLaunchTickingLocked();
if (task != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4339e513..f58b83d 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -179,6 +179,7 @@
static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
+ static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16;
// Used to indicate that windows of activities should be preserved during the resize.
static final boolean PRESERVE_WINDOWS = true;
@@ -598,7 +599,8 @@
}
}
- void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) {
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime,
+ @WaitResult.LaunchState int launchState) {
boolean changed = false;
for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
WaitResult w = mWaitingActivityLaunched.remove(i);
@@ -609,6 +611,7 @@
w.who = new ComponentName(r.info.packageName, r.info.name);
}
w.totalTime = totalTime;
+ w.launchState = launchState;
// Do not modify w.result.
}
}
@@ -793,7 +796,7 @@
System.identityHashCode(r), task.taskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
- mService.mHomeProcess = task.mActivities.get(0).app;
+ updateHomeProcess(task.mActivities.get(0).app);
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -915,6 +918,15 @@
return true;
}
+ void updateHomeProcess(WindowProcessController app) {
+ if (app != null && mService.mHomeProcess != app) {
+ if (!mHandler.hasMessages(REPORT_HOME_CHANGED_MSG)) {
+ mHandler.sendEmptyMessage(REPORT_HOME_CHANGED_MSG);
+ }
+ mService.mHomeProcess = app;
+ }
+ }
+
private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
int extrasSize = 0;
if (intent != null) {
@@ -1242,7 +1254,8 @@
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY);
+ reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY,
+ -1 /* launchState */);
}
// This is a hack to semi-deal with a race condition
@@ -2540,7 +2553,15 @@
}
}
} break;
+ case REPORT_HOME_CHANGED_MSG: {
+ synchronized (mService.mGlobalLock) {
+ mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
+ // Start home activities on displays with no activities.
+ mRootActivityContainer.startHomeOnEmptyDisplays("homeChanged");
+ }
+ }
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index c5b42f9..84a32fc 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -336,6 +336,15 @@
return homeStarted;
}
+ void startHomeOnEmptyDisplays(String reason) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display.topRunningActivity() == null) {
+ startHomeOnDisplay(mCurrentUser, reason, display.mDisplayId);
+ }
+ }
+ }
+
/**
* This starts home activity on displays that can have system decorations and only if the
* home activity can have multiple instances.
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 649f1a5..4d4a7b4 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -34,7 +34,6 @@
#include "netdbpf/BpfNetworkStats.h"
using android::bpf::Stats;
-using android::bpf::hasBpfSupport;
using android::bpf::bpfGetUidStats;
using android::bpf::bpfGetIfaceStats;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 88f645d..e1b83fc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -109,6 +109,7 @@
import com.android.server.os.SchedulingPolicyService;
import com.android.server.pm.BackgroundDexOptService;
import com.android.server.pm.CrossProfileAppsService;
+import com.android.server.pm.DynamicCodeLoggingService;
import com.android.server.pm.Installer;
import com.android.server.pm.LauncherAppsService;
import com.android.server.pm.OtaDexoptService;
@@ -1667,6 +1668,18 @@
traceEnd();
if (!isWatch) {
+ // We don't run this on watches as there are no plans to use the data logged
+ // on watch devices.
+ traceBeginAndSlog("StartDynamicCodeLoggingService");
+ try {
+ DynamicCodeLoggingService.schedule(context);
+ } catch (Throwable e) {
+ reportWtf("starting DynamicCodeLoggingService", e);
+ }
+ traceEnd();
+ }
+
+ if (!isWatch) {
traceBeginAndSlog("StartPruneInstantAppsJobService");
try {
PruneInstantAppsJobService.schedule(context);
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index a7209a0..f037905 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -111,7 +111,7 @@
* the last writable 32bit word.
*/
@VisibleForTesting
- private static enum Counter {
+ public static enum Counter {
RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds)
TOTAL_PACKETS,
PASSED_ARP,
@@ -139,7 +139,8 @@
DROPPED_IPV6_MULTICAST_PING,
DROPPED_IPV6_NON_ICMP_MULTICAST,
DROPPED_802_3_FRAME,
- DROPPED_ETHERTYPE_BLACKLISTED;
+ DROPPED_ETHERTYPE_BLACKLISTED,
+ DROPPED_ARP_REPLY_SPA_NO_HOST;
// Returns the negative byte offset from the end of the APF data segment for
// a given counter.
@@ -156,7 +157,7 @@
/**
* When APFv4 is supported, loads R1 with the offset of the specified counter.
*/
- private void maybeSetCounter(ApfGenerator gen, Counter c) {
+ private void maybeSetupCounter(ApfGenerator gen, Counter c) {
if (mApfCapabilities.hasDataAccess()) {
gen.addLoadImmediate(Register.R1, c.offset());
}
@@ -288,16 +289,18 @@
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
- private static final short ARP_OPCODE_REQUEST = 1;
- private static final short ARP_OPCODE_REPLY = 2;
private static final byte[] ARP_IPV4_HEADER = {
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
6, // Hardware size: 6
4, // Protocol size: 4
};
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
+ // Opcode: ARP request (0x0001), ARP reply (0x0002)
+ private static final short ARP_OPCODE_REQUEST = 1;
+ private static final short ARP_OPCODE_REPLY = 2;
+ private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
+ private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
// Do not log ApfProgramEvents whose actual lifetimes was less than this.
private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
// Limit on the Black List size to cap on program usage for this
@@ -816,7 +819,7 @@
gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
}
}
- maybeSetCounter(gen, Counter.DROPPED_RA);
+ maybeSetupCounter(gen, Counter.DROPPED_RA);
gen.addJump(mCountAndDropLabel);
gen.defineLabel(nextFilterLabel);
return filterLifetime;
@@ -883,6 +886,8 @@
// pass
// if not ARP IPv4 reply or request
// pass
+ // if ARP reply source ip is 0.0.0.0
+ // drop
// if unicast ARP reply
// pass
// if interface has no IPv4 address
@@ -897,18 +902,23 @@
// Pass if not ARP IPv4.
gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
- maybeSetCounter(gen, Counter.PASSED_ARP_NON_IPV4);
+ maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4);
gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
// Pass if unknown ARP opcode.
gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
- maybeSetCounter(gen, Counter.PASSED_ARP_UNKNOWN);
+ maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN);
gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
+ // Drop if ARP reply source IP is 0.0.0.0
+ gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET);
+ maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST);
+ gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
+
// Pass if unicast reply.
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
+ maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
// Either a unicast request, a unicast reply, or a broadcast reply.
@@ -916,17 +926,17 @@
if (mIPv4Address == null) {
// When there is no IPv4 address, drop GARP replies (b/29404209).
gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetCounter(gen, Counter.DROPPED_GARP_REPLY);
+ maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY);
gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
} else {
// When there is an IPv4 address, drop unicast/broadcast requests
// and broadcast replies with a different target IPv4 address.
gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
+ maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
}
- maybeSetCounter(gen, Counter.PASSED_ARP);
+ maybeSetupCounter(gen, Counter.PASSED_ARP);
gen.addJump(mCountAndPassLabel);
}
@@ -970,7 +980,7 @@
// NOTE: Relies on R1 containing IPv4 header offset.
gen.addAddR1();
gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
- maybeSetCounter(gen, Counter.PASSED_DHCP);
+ maybeSetupCounter(gen, Counter.PASSED_DHCP);
gen.addJump(mCountAndPassLabel);
// Drop all multicasts/broadcasts.
@@ -979,30 +989,30 @@
// If IPv4 destination address is in multicast range, drop.
gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
gen.addAnd(0xf0);
- maybeSetCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
// If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
- maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
if (mIPv4Address != null && mIPv4PrefixLength < 31) {
- maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
}
// If L2 broadcast packet, drop.
// TODO: can we invert this condition to fall through to the common pass case below?
- maybeSetCounter(gen, Counter.PASSED_IPV4_UNICAST);
+ maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
gen.addJump(mCountAndDropLabel);
}
// Otherwise, pass
- maybeSetCounter(gen, Counter.PASSED_IPV4);
+ maybeSetupCounter(gen, Counter.PASSED_IPV4);
gen.addJump(mCountAndPassLabel);
}
@@ -1050,16 +1060,16 @@
// Drop all other packets sent to ff00::/8 (multicast prefix).
gen.defineLabel(dropAllIPv6MulticastsLabel);
- maybeSetCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
// Not multicast. Pass.
- maybeSetCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
+ maybeSetupCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
gen.addJump(mCountAndPassLabel);
gen.defineLabel(skipIPv6MulticastFilterLabel);
} else {
// If not ICMPv6, pass.
- maybeSetCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
+ maybeSetupCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
}
@@ -1069,7 +1079,7 @@
String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
// Drop all router solicitations (b/32833400)
- maybeSetCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
// If not neighbor announcements, skip filter.
gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
@@ -1078,7 +1088,7 @@
gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
skipUnsolicitedMulticastNALabel);
- maybeSetCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
+ maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
gen.addJump(mCountAndDropLabel);
gen.defineLabel(skipUnsolicitedMulticastNALabel);
}
@@ -1108,7 +1118,7 @@
if (mApfCapabilities.hasDataAccess()) {
// Increment TOTAL_PACKETS
- maybeSetCounter(gen, Counter.TOTAL_PACKETS);
+ maybeSetupCounter(gen, Counter.TOTAL_PACKETS);
gen.addLoadData(Register.R0, 0); // load counter
gen.addAdd(1);
gen.addStoreData(Register.R0, 0); // write-back counter
@@ -1134,12 +1144,12 @@
if (mDrop802_3Frames) {
// drop 802.3 frames (ethtype < 0x0600)
- maybeSetCounter(gen, Counter.DROPPED_802_3_FRAME);
+ maybeSetupCounter(gen, Counter.DROPPED_802_3_FRAME);
gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
}
// Handle ether-type black list
- maybeSetCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
+ maybeSetupCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
for (int p : mEthTypeBlackList) {
gen.addJumpIfR0Equals(p, mCountAndDropLabel);
}
@@ -1168,9 +1178,9 @@
// Drop non-IP non-ARP broadcasts, pass the rest
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetCounter(gen, Counter.PASSED_NON_IP_UNICAST);
+ maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST);
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetCounter(gen, Counter.DROPPED_ETH_BROADCAST);
+ maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST);
gen.addJump(mCountAndDropLabel);
// Add IPv6 filters:
@@ -1193,7 +1203,7 @@
// Execution will reach the bottom of the program if none of the filters match,
// which will pass the packet to the application processor.
- maybeSetCounter(gen, Counter.PASSED_IPV6_ICMP);
+ maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP);
// Append the count & pass trampoline, which increments the counter at the data address
// pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 71aec23..effb5a7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -270,6 +270,60 @@
}
@Test
+ public void testOnAppRemovedLocked() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test.remove",
+ createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test.remove",
+ createTimingSession(
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test.remove",
+ createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ // Test that another app isn't affected.
+ TimingSession one = createTimingSession(
+ now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
+ TimingSession two = createTimingSession(
+ now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+ List<TimingSession> expected = new ArrayList<>();
+ // Added in correct (chronological) order.
+ expected.add(two);
+ expected.add(one);
+ mQuotaController.saveTimingSession(0, "com.android.test.stay", two);
+ mQuotaController.saveTimingSession(0, "com.android.test.stay", one);
+
+ mQuotaController.onAppRemovedLocked("com.android.test.remove", 10001);
+ assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
+ assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
+ }
+
+ @Test
+ public void testOnUserRemovedLocked() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ // Test that another user isn't affected.
+ TimingSession one = createTimingSession(
+ now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
+ TimingSession two = createTimingSession(
+ now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+ List<TimingSession> expected = new ArrayList<>();
+ // Added in correct (chronological) order.
+ expected.add(two);
+ expected.add(one);
+ mQuotaController.saveTimingSession(10, "com.android.test", two);
+ mQuotaController.saveTimingSession(10, "com.android.test", one);
+
+ mQuotaController.onUserRemovedLocked(0);
+ assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test"));
+ }
+
+ @Test
public void testGetTrailingExecutionTimeLocked_NoTimer() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Added in chronological order.
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d7b1cb4..4ee9551 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -70,7 +70,7 @@
"liblzma",
"libnativehelper",
"libui",
- "libunwind",
+ "libunwindstack",
"libutils",
"netd_aidl_interface-cpp",
],
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index e6b328a..ec5d93e 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -29,8 +29,7 @@
import android.os.UserManagerInternal;
import android.os.storage.StorageManagerInternal;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.Zygote;
import org.junit.Before;
import org.junit.Test;
@@ -38,6 +37,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StorageManagerServiceTest {
@@ -97,15 +99,15 @@
when(mPm.getPackagesForUid(eq(UID_GREY))).thenReturn(new String[] { PKG_GREY });
when(mPm.getPackagesForUid(eq(UID_COLORS))).thenReturn(new String[] { PKG_RED, PKG_BLUE });
- setIsAppStorageSandboxed(PID_BLUE, UID_COLORS, true);
- setIsAppStorageSandboxed(PID_GREY, UID_GREY, true);
- setIsAppStorageSandboxed(PID_RED, UID_COLORS, true);
+ setStorageMountMode(PID_BLUE, UID_COLORS, Zygote.MOUNT_EXTERNAL_WRITE);
+ setStorageMountMode(PID_GREY, UID_GREY, Zygote.MOUNT_EXTERNAL_WRITE);
+ setStorageMountMode(PID_RED, UID_COLORS, Zygote.MOUNT_EXTERNAL_WRITE);
mService = new StorageManagerService(mContext);
}
- private void setIsAppStorageSandboxed(int pid, int uid, boolean sandboxed) {
- when(mAmi.isAppStorageSandboxed(pid, uid)).thenReturn(sandboxed);
+ private void setStorageMountMode(int pid, int uid, int mountMode) {
+ when(mAmi.getStorageMountMode(pid, uid)).thenReturn(mountMode);
}
@Test
@@ -210,7 +212,7 @@
@Test
public void testPackageNotSandboxed() throws Exception {
- setIsAppStorageSandboxed(PID_RED, UID_COLORS, false);
+ setStorageMountMode(PID_RED, UID_COLORS, Zygote.MOUNT_EXTERNAL_FULL);
// Both app and system have the same view
assertTranslation(
@@ -224,6 +226,29 @@
PID_RED, UID_COLORS);
}
+ @Test
+ public void testInstallerPackage() throws Exception {
+ setStorageMountMode(PID_GREY, UID_GREY, Zygote.MOUNT_EXTERNAL_INSTALLER);
+
+ assertTranslation(
+ "/storage/emulated/0/Android/obb/com.grey/foo.jpg",
+ "/storage/emulated/0/Android/obb/com.grey/foo.jpg",
+ PID_GREY, UID_GREY);
+ assertTranslation(
+ "/storage/emulated/0/Android/obb/com.blue/bar.jpg",
+ "/storage/emulated/0/Android/obb/com.blue/bar.jpg",
+ PID_GREY, UID_GREY);
+
+ assertTranslation(
+ "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+ "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+ PID_GREY, UID_GREY);
+ assertTranslation(
+ "/storage/emulated/0/Android/sandbox/com.grey/Android/data/com.blue/bar.jpg",
+ "/storage/emulated/0/Android/data/com.blue/bar.jpg",
+ PID_GREY, UID_GREY);
+ }
+
private void assertTranslation(String system, String sandbox,
int pid, int uid) throws Exception {
assertEquals(system,
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
index 87c3cd2..3b6b48b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
@@ -16,14 +16,20 @@
package com.android.server.pm.dex;
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.os.storage.StorageManager;
import androidx.test.filters.SmallTest;
@@ -43,13 +49,12 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
-
-import java.util.Arrays;
+import org.mockito.stubbing.Stubber;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DexLoggerTests {
- private static final String PACKAGE_NAME = "package.name";
+ private static final String OWNING_PACKAGE_NAME = "package.name";
private static final String VOLUME_UUID = "volUuid";
private static final String DEX_PATH = "/bar/foo.jar";
private static final int STORAGE_FLAGS = StorageManager.FLAG_STORAGE_DE;
@@ -66,6 +71,7 @@
};
private static final String CONTENT_HASH =
"0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20";
+ private static final byte[] EMPTY_BYTES = {};
@Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
@@ -73,92 +79,191 @@
@Mock Installer mInstaller;
private final Object mInstallLock = new Object();
- private DexManager.Listener mListener;
+ private PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+ private DexLogger mDexLogger;
private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create();
+ private boolean mWriteTriggered = false;
+ private static final String EXPECTED_MESSAGE_WITH_CONTENT_HASH =
+ DEX_FILENAME_HASH + " " + CONTENT_HASH;
@Before
- public void setup() {
+ public void setup() throws Exception {
+ // Disable actually attempting to do file writes.
+ mPackageDynamicCodeLoading = new PackageDynamicCodeLoading() {
+ @Override
+ void maybeWriteAsync() {
+ mWriteTriggered = true;
+ }
+
+ @Override
+ protected void writeNow(Void data) {
+ throw new AssertionError("These tests should never call this method.");
+ }
+ };
+
// For test purposes capture log messages as well as sending to the event log.
- mListener = new DexLogger(mPM, mInstaller, mInstallLock) {
- @Override
+ mDexLogger = new DexLogger(mPM, mInstaller, mInstallLock, mPackageDynamicCodeLoading) {
+ @Override
void writeDclEvent(int uid, String message) {
super.writeDclEvent(uid, message);
mMessagesForUid.put(uid, message);
}
};
+
+ // Make the owning package exist in our mock PackageManager.
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.deviceProtectedDataDir = "/bar";
+ appInfo.uid = OWNER_UID;
+ appInfo.volumeUuid = VOLUME_UUID;
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = appInfo;
+
+ doReturn(packageInfo).when(mPM)
+ .getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID);
}
@Test
- public void testSingleAppWithFileHash() throws Exception {
- doReturn(CONTENT_HASH_BYTES).when(mInstaller).hashSecondaryDexFile(
- DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+ public void testOneLoader_ownFile_withFileHash() throws Exception {
+ whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
- runOnReconcile();
+ recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
- assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID);
- String expectedMessage = DEX_FILENAME_HASH + " " + CONTENT_HASH;
- assertThat(mMessagesForUid).containsEntry(OWNER_UID, expectedMessage);
+ assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
+ assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+
+ assertThat(mWriteTriggered).isFalse();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+ .containsExactly(OWNING_PACKAGE_NAME);
}
@Test
- public void testSingleAppNoFileHash() throws Exception {
- doReturn(new byte[] { }).when(mInstaller).hashSecondaryDexFile(
- DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+ public void testOneLoader_ownFile_noFileHash() throws Exception {
+ whenFileIsHashed(DEX_PATH, doReturn(EMPTY_BYTES));
- runOnReconcile();
+ recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
- assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID);
+ assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+
+ // File should be removed from the DCL list, since we can't hash it.
+ assertThat(mWriteTriggered).isTrue();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
}
@Test
- public void testSingleAppHashFails() throws Exception {
- doThrow(new InstallerException("Testing failure")).when(mInstaller).hashSecondaryDexFile(
- DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+ public void testOneLoader_ownFile_hashingFails() throws Exception {
+ whenFileIsHashed(DEX_PATH, doThrow(new InstallerException("Intentional failure for test")));
- runOnReconcile();
+ recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+ assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
+ assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+
+ // File should be removed from the DCL list, since we can't hash it.
+ assertThat(mWriteTriggered).isTrue();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+ }
+
+ @Test
+ public void testOneLoader_ownFile_unknownPath() {
+ recordLoad(OWNING_PACKAGE_NAME, "other/path");
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid).isEmpty();
+ assertThat(mWriteTriggered).isTrue();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
}
@Test
- public void testOtherApps() throws Exception {
- doReturn(CONTENT_HASH_BYTES).when(mInstaller).hashSecondaryDexFile(
- DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+ public void testOneLoader_differentOwner() throws Exception {
+ whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ setPackageUid("other.package.name", 1001);
- // Simulate three packages from two different UIDs
- String packageName1 = "other1.package.name";
- String packageName2 = "other2.package.name";
- String packageName3 = "other3.package.name";
- int uid1 = 1001;
- int uid2 = 1002;
+ recordLoad("other.package.name", DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
- doReturn(uid1).when(mPM).getPackageUid(packageName1, 0, OWNER_USER_ID);
- doReturn(uid2).when(mPM).getPackageUid(packageName2, 0, OWNER_USER_ID);
- doReturn(uid1).when(mPM).getPackageUid(packageName3, 0, OWNER_USER_ID);
-
- runOnReconcile(packageName1, packageName2, packageName3);
-
- assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID, uid1, uid2);
-
- String expectedMessage = DEX_FILENAME_HASH + " " + CONTENT_HASH;
- assertThat(mMessagesForUid).containsEntry(OWNER_UID, expectedMessage);
- assertThat(mMessagesForUid).containsEntry(uid1, expectedMessage);
- assertThat(mMessagesForUid).containsEntry(uid2, expectedMessage);
+ assertThat(mMessagesForUid.keys()).containsExactly(1001);
+ assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+ assertThat(mWriteTriggered).isFalse();
}
- private void runOnReconcile(String... otherPackageNames) {
- ApplicationInfo appInfo = new ApplicationInfo();
- appInfo.packageName = PACKAGE_NAME;
- appInfo.volumeUuid = VOLUME_UUID;
- appInfo.uid = OWNER_UID;
+ @Test
+ public void testOneLoader_differentOwner_uninstalled() throws Exception {
+ whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ setPackageUid("other.package.name", -1);
- boolean isUsedByOtherApps = otherPackageNames.length > 0;
- DexUseInfo dexUseInfo = new DexUseInfo(
- isUsedByOtherApps, OWNER_USER_ID, /* classLoaderContext */ null, /* loaderIsa */ null);
- dexUseInfo.getLoadingPackages().addAll(Arrays.asList(otherPackageNames));
+ recordLoad("other.package.name", DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
- mListener.onReconcileSecondaryDexFile(appInfo, dexUseInfo, DEX_PATH, STORAGE_FLAGS);
+ assertThat(mMessagesForUid).isEmpty();
+ assertThat(mWriteTriggered).isFalse();
+ }
+
+ @Test
+ public void testMultipleLoadersAndFiles() throws Exception {
+ String otherDexPath = "/bar/nosuchdir/foo.jar";
+ whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ whenFileIsHashed(otherDexPath, doReturn(EMPTY_BYTES));
+ setPackageUid("other.package.name1", 1001);
+ setPackageUid("other.package.name2", 1002);
+
+ recordLoad("other.package.name1", DEX_PATH);
+ recordLoad("other.package.name1", otherDexPath);
+ recordLoad("other.package.name2", DEX_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+ assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID);
+ assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+ assertThat(mMessagesForUid).containsEntry(1001, DEX_FILENAME_HASH);
+ assertThat(mMessagesForUid).containsEntry(1002, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+ assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+
+ assertThat(mWriteTriggered).isTrue();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+ .containsExactly(OWNING_PACKAGE_NAME);
+
+ // Check the DexLogger caching is working
+ verify(mPM, atMost(1)).getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID);
+ }
+
+ @Test
+ public void testUnknownOwner() {
+ reset(mPM);
+ recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ mDexLogger.logDynamicCodeLoading("other.package.name");
+
+ assertThat(mMessagesForUid).isEmpty();
+ assertThat(mWriteTriggered).isFalse();
+ verifyZeroInteractions(mPM);
+ }
+
+ @Test
+ public void testUninstalledPackage() {
+ reset(mPM);
+ recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+ assertThat(mMessagesForUid).isEmpty();
+ assertThat(mWriteTriggered).isTrue();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+ }
+
+ private void setPackageUid(String packageName, int uid) throws Exception {
+ doReturn(uid).when(mPM).getPackageUid(packageName, /*flags*/ 0, OWNER_USER_ID);
+ }
+
+ private void whenFileIsHashed(String dexPath, Stubber stubber) throws Exception {
+ stubber.when(mInstaller).hashSecondaryDexFile(
+ dexPath, OWNING_PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+ }
+
+ private void recordLoad(String loadingPackageName, String dexPath) {
+ mPackageDynamicCodeLoading.record(
+ OWNING_PACKAGE_NAME, dexPath, FILE_TYPE_DEX, OWNER_USER_ID, loadingPackageName);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index fd07cb0..7cd8cedd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -27,12 +27,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -78,7 +72,6 @@
@Mock Installer mInstaller;
@Mock IPackageManager mPM;
private final Object mInstallLock = new Object();
- @Mock DexManager.Listener mListener;
private DexManager mDexManager;
@@ -114,9 +107,8 @@
mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
- mDexManager = new DexManager(
- /*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock,
- mListener);
+ mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
+ mInstaller, mInstallLock);
// Foo and Bar are available to user0.
// Only Bar is available to user1;
@@ -415,9 +407,10 @@
String frameworkDex = "/system/framework/com.android.location.provider.jar";
// Load a dex file from framework.
notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
- // The dex file should not be recognized as a package.
- assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
- assertNull(mDexManager.getPackageDynamicCodeInfo(frameworkDex));
+ // The dex file should not be recognized as owned by the package.
+ assertFalse(mDexManager.hasInfoOnPackage(mFooUser0.getPackageName()));
+
+ assertNull(getPackageDynamicCodeInfo(mFooUser0));
}
@Test
@@ -510,21 +503,6 @@
assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
}
- @Test
- public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
- List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
- notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
-
- when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
- .thenReturn(mFooUser0.mPackageInfo);
-
- mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
-
- verify(mListener, times(fooSecondaries.size()))
- .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
- any(DexUseInfo.class), anyString(), anyInt());
- }
-
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
@@ -585,6 +563,10 @@
return pui;
}
+ private PackageDynamicCode getPackageDynamicCodeInfo(TestData testData) {
+ return mDexManager.getDexLogger().getPackageDynamicCodeInfo(testData.getPackageName());
+ }
+
private void assertNoUseInfo(TestData testData) {
assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
}
@@ -600,11 +582,11 @@
}
private void assertNoDclInfo(TestData testData) {
- assertNull(mDexManager.getPackageDynamicCodeInfo(testData.getPackageName()));
+ assertNull(getPackageDynamicCodeInfo(testData));
}
private void assertNoDclInfo(TestData testData, int userId) {
- PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(testData.getPackageName());
+ PackageDynamicCode info = getPackageDynamicCodeInfo(testData);
if (info == null) {
return;
}
@@ -615,7 +597,7 @@
}
private void assertHasDclInfo(TestData owner, TestData loader, List<String> paths) {
- PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(owner.getPackageName());
+ PackageDynamicCode info = getPackageDynamicCodeInfo(owner);
assertNotNull("No DCL data for owner " + owner.getPackageName(), info);
for (String path : paths) {
DynamicCodeFile fileInfo = info.mFileUsageMap.get(path);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
index eb4cc4e..f4cdc8cd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
@@ -16,9 +16,12 @@
package com.android.server.pm.dex;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.MAX_FILES_PER_OWNER;
import static com.android.server.pm.dex.PackageDynamicCodeLoading.escape;
import static com.android.server.pm.dex.PackageDynamicCodeLoading.unescape;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -119,6 +122,24 @@
}
@Test
+ public void testRecord_tooManyFiles_ignored() {
+ PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+ int tooManyFiles = MAX_FILES_PER_OWNER + 1;
+ for (int i = 1; i <= tooManyFiles; i++) {
+ Entry entry = new Entry("owning.package", "/path/file" + i, 'D', 10, "loading.package");
+ boolean added = record(info, entry);
+ Set<Entry> entries = entriesFrom(info);
+ if (i < tooManyFiles) {
+ assertThat(entries).contains(entry);
+ assertTrue(added);
+ } else {
+ assertThat(entries).doesNotContain(entry);
+ assertFalse(added);
+ }
+ }
+ }
+
+ @Test
public void testClear() {
Entry[] entries = {
new Entry("owner1", "file1", 'D', 10, "loader1"),
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 49030f5..fb371c1 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -476,7 +476,7 @@
/**
* A boolean meta-data value indicating whether an {@link InCallService} implements an
* in-call user interface to be used while the device is in car-mode (see
- * {@link android.content.res.Configuration.UI_MODE_TYPE_CAR}).
+ * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}).
*/
public static final String METADATA_IN_CALL_SERVICE_CAR_MODE_UI =
"android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cfe134fd..388b5fb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1398,9 +1398,9 @@
* Example: "default"
*
* {@code ERROR_CODE_1} is an integer defined in
- * {@link com.android.internal.telephony.dataconnection.DcFailCause DcFailure}
+ * {@link DataFailCause DcFailure}
* Example:
- * {@link com.android.internal.telephony.dataconnection.DcFailCause#MISSING_UNKNOWN_APN}
+ * {@link DataFailCause#MISSING_UNKNOWN_APN}
*
* {@code CARRIER_ACTION_IDX_1} is an integer defined in
* {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
new file mode 100644
index 0000000..c6f7d0e
--- /dev/null
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2006 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.telephony;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Returned as the reason for a connection failure as defined
+ * by RIL_DataCallFailCause in ril.h and some local errors.
+ * @hide
+ */
+public enum DataFailCause {
+ NONE(0),
+
+ // This series of errors as specified by the standards
+ // specified in ril.h
+ OPERATOR_BARRED(0x08), /* no retry */
+ NAS_SIGNALLING(0x0E),
+ LLC_SNDCP(0x19),
+ INSUFFICIENT_RESOURCES(0x1A),
+ MISSING_UNKNOWN_APN(0x1B), /* no retry */
+ UNKNOWN_PDP_ADDRESS_TYPE(0x1C), /* no retry */
+ USER_AUTHENTICATION(0x1D), /* no retry */
+ ACTIVATION_REJECT_GGSN(0x1E), /* no retry */
+ ACTIVATION_REJECT_UNSPECIFIED(0x1F),
+ SERVICE_OPTION_NOT_SUPPORTED(0x20), /* no retry */
+ SERVICE_OPTION_NOT_SUBSCRIBED(0x21), /* no retry */
+ SERVICE_OPTION_OUT_OF_ORDER(0x22),
+ NSAPI_IN_USE(0x23), /* no retry */
+ REGULAR_DEACTIVATION(0x24), /* possibly restart radio, based on config */
+ QOS_NOT_ACCEPTED(0x25),
+ NETWORK_FAILURE(0x26),
+ UMTS_REACTIVATION_REQ(0x27),
+ FEATURE_NOT_SUPP(0x28),
+ TFT_SEMANTIC_ERROR(0x29),
+ TFT_SYTAX_ERROR(0x2A),
+ UNKNOWN_PDP_CONTEXT(0x2B),
+ FILTER_SEMANTIC_ERROR(0x2C),
+ FILTER_SYTAX_ERROR(0x2D),
+ PDP_WITHOUT_ACTIVE_TFT(0x2E),
+ ONLY_IPV4_ALLOWED(0x32), /* no retry */
+ ONLY_IPV6_ALLOWED(0x33), /* no retry */
+ ONLY_SINGLE_BEARER_ALLOWED(0x34),
+ ESM_INFO_NOT_RECEIVED(0x35),
+ PDN_CONN_DOES_NOT_EXIST(0x36),
+ MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37),
+ MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41),
+ UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42),
+ INVALID_TRANSACTION_ID(0x51),
+ MESSAGE_INCORRECT_SEMANTIC(0x5F),
+ INVALID_MANDATORY_INFO(0x60),
+ MESSAGE_TYPE_UNSUPPORTED(0x61),
+ MSG_TYPE_NONCOMPATIBLE_STATE(0x62),
+ UNKNOWN_INFO_ELEMENT(0x63),
+ CONDITIONAL_IE_ERROR(0x64),
+ MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65),
+ PROTOCOL_ERRORS(0x6F), /* no retry */
+ APN_TYPE_CONFLICT(0x70),
+ INVALID_PCSCF_ADDR(0x71),
+ INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72),
+ EMM_ACCESS_BARRED(0x73),
+ EMERGENCY_IFACE_ONLY(0x74),
+ IFACE_MISMATCH(0x75),
+ COMPANION_IFACE_IN_USE(0x76),
+ IP_ADDRESS_MISMATCH(0x77),
+ IFACE_AND_POL_FAMILY_MISMATCH(0x78),
+ EMM_ACCESS_BARRED_INFINITE_RETRY(0x79),
+ AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A),
+
+ // OEM sepecific error codes. To be used by OEMs when they don't
+ // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
+ OEM_DCFAILCAUSE_1(0x1001),
+ OEM_DCFAILCAUSE_2(0x1002),
+ OEM_DCFAILCAUSE_3(0x1003),
+ OEM_DCFAILCAUSE_4(0x1004),
+ OEM_DCFAILCAUSE_5(0x1005),
+ OEM_DCFAILCAUSE_6(0x1006),
+ OEM_DCFAILCAUSE_7(0x1007),
+ OEM_DCFAILCAUSE_8(0x1008),
+ OEM_DCFAILCAUSE_9(0x1009),
+ OEM_DCFAILCAUSE_10(0x100A),
+ OEM_DCFAILCAUSE_11(0x100B),
+ OEM_DCFAILCAUSE_12(0x100C),
+ OEM_DCFAILCAUSE_13(0x100D),
+ OEM_DCFAILCAUSE_14(0x100E),
+ OEM_DCFAILCAUSE_15(0x100F),
+
+ // Local errors generated by Vendor RIL
+ // specified in ril.h
+ REGISTRATION_FAIL(-1),
+ GPRS_REGISTRATION_FAIL(-2),
+ SIGNAL_LOST(-3), /* no retry */
+ PREF_RADIO_TECH_CHANGED(-4),
+ RADIO_POWER_OFF(-5), /* no retry */
+ TETHERED_CALL_ACTIVE(-6), /* no retry */
+ ERROR_UNSPECIFIED(0xFFFF),
+
+ // Errors generated by the Framework
+ // specified here
+ UNKNOWN(0x10000),
+ RADIO_NOT_AVAILABLE(0x10001), /* no retry */
+ UNACCEPTABLE_NETWORK_PARAMETER(0x10002), /* no retry */
+ CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003),
+ LOST_CONNECTION(0x10004),
+ RESET_BY_FRAMEWORK(0x10005);
+
+ private final int mErrorCode;
+ private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap;
+ static {
+ sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>();
+ for (DataFailCause fc : values()) {
+ sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
+ }
+ }
+
+ /**
+ * Map of subId -> set of data call setup permanent failure for the carrier.
+ */
+ private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache =
+ new HashMap<>();
+
+ DataFailCause(int errorCode) {
+ mErrorCode = errorCode;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /**
+ * Returns whether or not the fail cause is a failure that requires a modem restart
+ *
+ * @param context device context
+ * @param subId subscription index
+ * @return true if the fail cause code needs platform to trigger a modem restart.
+ */
+ public boolean isRadioRestartFailure(Context context, int subId) {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfigForSubId(subId);
+
+ if (b != null) {
+ if (this == REGULAR_DEACTIVATION
+ && b.getBoolean(CarrierConfigManager
+ .KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
+ // This is for backward compatibility support. We need to continue support this
+ // old configuration until it gets removed in the future.
+ return true;
+ }
+ // Check the current configurations.
+ int[] causeCodes = b.getIntArray(CarrierConfigManager
+ .KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
+ if (causeCodes != null) {
+ return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode());
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isPermanentFailure(Context context, int subId) {
+
+ synchronized (sPermanentFailureCache) {
+
+ HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+
+ // In case of cache miss, we need to look up the settings from carrier config.
+ if (permanentFailureSet == null) {
+ // Retrieve the permanent failure from carrier config
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfigForSubId(subId);
+ if (b != null) {
+ String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
+ KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
+
+ if (permanentFailureStrings != null) {
+ permanentFailureSet = new HashSet<>();
+ for (String failure : permanentFailureStrings) {
+ permanentFailureSet.add(DataFailCause.valueOf(failure));
+ }
+ }
+ }
+ }
+
+ // If we are not able to find the configuration from carrier config, use the default
+ // ones.
+ if (permanentFailureSet == null) {
+ permanentFailureSet = new HashSet<DataFailCause>() {
+ {
+ add(OPERATOR_BARRED);
+ add(MISSING_UNKNOWN_APN);
+ add(UNKNOWN_PDP_ADDRESS_TYPE);
+ add(USER_AUTHENTICATION);
+ add(ACTIVATION_REJECT_GGSN);
+ add(SERVICE_OPTION_NOT_SUPPORTED);
+ add(SERVICE_OPTION_NOT_SUBSCRIBED);
+ add(NSAPI_IN_USE);
+ add(ONLY_IPV4_ALLOWED);
+ add(ONLY_IPV6_ALLOWED);
+ add(PROTOCOL_ERRORS);
+ add(RADIO_POWER_OFF);
+ add(TETHERED_CALL_ACTIVE);
+ add(RADIO_NOT_AVAILABLE);
+ add(UNACCEPTABLE_NETWORK_PARAMETER);
+ add(SIGNAL_LOST);
+ }
+ };
+ }
+
+ sPermanentFailureCache.put(subId, permanentFailureSet);
+ }
+
+ return permanentFailureSet.contains(this);
+ }
+ }
+
+ public boolean isEventLoggable() {
+ return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
+ (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
+ (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
+ (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
+ (this == SERVICE_OPTION_NOT_SUPPORTED) ||
+ (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
+ (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
+ (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) ||
+ (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
+ (this == UNACCEPTABLE_NETWORK_PARAMETER);
+ }
+
+ public static DataFailCause fromInt(int errorCode) {
+ DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
+ if (fc == null) {
+ fc = UNKNOWN;
+ }
+ return fc;
+ }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index c95837e..4dcb410 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -20,7 +20,6 @@
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import com.android.i18n.phonenumbers.ShortNumberInfo;
import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
@@ -2185,101 +2184,6 @@
}
/**
- * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose.
- *
- * @hide
- */
- public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch,
- String defaultCountryIso) {
- // If the number passed in is null, just return false:
- if (number == null) return false;
-
- // If the number passed in is a SIP address, return false, since the
- // concept of "emergency numbers" is only meaningful for calls placed
- // over the cell network.
- // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
- // since the whole point of extractNetworkPortionAlt() is to filter out
- // any non-dialable characters (which would turn 'abc911def@example.com'
- // into '911', for example.))
- if (PhoneNumberUtils.isUriNumber(number)) {
- return false;
- }
-
- // Strip the separators from the number before comparing it
- // to the list.
- number = PhoneNumberUtils.extractNetworkPortionAlt(number);
-
- String emergencyNumbers = "";
- int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId());
-
- // retrieve the list of emergency numbers
- // check read-write ecclist property first
- String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-
- emergencyNumbers = SystemProperties.get(ecclist, "");
-
- Rlog.d(LOG_TAG, "slotId:" + slotId + " country:"
- + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers);
-
- if (TextUtils.isEmpty(emergencyNumbers)) {
- // then read-only ecclist property since old RIL only uses this
- emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
- }
-
- if (!TextUtils.isEmpty(emergencyNumbers)) {
- // searches through the comma-separated list for a match,
- // return true if one is found.
- for (String emergencyNum : emergencyNumbers.split(",")) {
- // It is not possible to append additional digits to an emergency number to dial
- // the number in Brazil - it won't connect.
- if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
- if (number.equals(emergencyNum)) {
- return true;
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- }
- }
- }
- // no matches found against the list!
- return false;
- }
-
- Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
- + " Use embedded logic for determining ones.");
-
- // If slot id is invalid, means that there is no sim card.
- // According spec 3GPP TS22.101, the following numbers should be
- // ECC numbers when SIM/USIM is not present.
- emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
-
- for (String emergencyNum : emergencyNumbers.split(",")) {
- if (useExactMatch) {
- if (number.equals(emergencyNum)) {
- return true;
- }
- } else {
- if (number.startsWith(emergencyNum)) {
- return true;
- }
- }
- }
-
- // No ecclist system property, so use our own list.
- if (defaultCountryIso != null) {
- ShortNumberInfo info = ShortNumberInfo.getInstance();
- if (useExactMatch) {
- return info.isEmergencyNumber(number, defaultCountryIso);
- } else {
- return info.connectsToEmergencyNumber(number, defaultCountryIso);
- }
- }
-
- return false;
- }
-
- /**
* isVoiceMailNumber: checks a given number against the voicemail
* number provided by the RIL and SIM card. The caller must have
* the READ_PHONE_STATE credential.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 348ab2a..11b6674 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5629,7 +5629,7 @@
if (value == null) {
value = "";
}
-
+ value.replace(',', ' ');
if (prop != null) {
p = prop.split(",");
}
@@ -5655,7 +5655,13 @@
}
}
- if (propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+ int propValLen = propVal.length();
+ try {
+ propValLen = propVal.getBytes("utf-8").length;
+ } catch (java.io.UnsupportedEncodingException e) {
+ Rlog.d(TAG, "setTelephonyProperty: utf-8 not supported");
+ }
+ if (propValLen > SystemProperties.PROP_VALUE_MAX) {
Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
" property=" + property + " value: " + value + " propVal=" + propVal);
return;
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index f124595..4d95e55 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -331,7 +331,80 @@
*/
public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
- /*
+ /**
+ * SIP Response : 405
+ * Method not allowed for the address in the Request URI
+ */
+ public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366;
+
+ /**
+ * SIP Response : 407
+ * The request requires user authentication
+ */
+ public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367;
+
+ /**
+ * SIP Response : 413
+ * Request body too large
+ */
+ public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368;
+
+ /**
+ * SIP Response : 414
+ * Request-URI too large
+ */
+ public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369;
+
+ /**
+ * SIP Response : 421
+ * Specific extension is required, which is not present in the HEADER
+ */
+ public static final int CODE_SIP_EXTENSION_REQUIRED = 370;
+
+ /**
+ * SIP Response : 422
+ * The session expiration field too small
+ */
+ public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371;
+
+ /**
+ * SIP Response : 481
+ * Request received by the server does not match any dialog or transaction
+ */
+ public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372;
+
+ /**
+ * SIP Response : 482
+ * Server has detected a loop
+ */
+ public static final int CODE_SIP_LOOP_DETECTED = 373;
+
+ /**
+ * SIP Response : 483
+ * Max-Forwards value reached
+ */
+ public static final int CODE_SIP_TOO_MANY_HOPS = 374;
+
+ /**
+ * SIP Response : 485
+ * Request-URI is ambiguous
+ *
+ */
+ public static final int CODE_SIP_AMBIGUOUS = 376;
+
+ /**
+ * SIP Response : 491
+ * Server has pending request for same dialog
+ */
+ public static final int CODE_SIP_REQUEST_PENDING = 377;
+
+ /**
+ * SIP Response : 493
+ * The request cannot be decrypted by recipient
+ */
+ public static final int CODE_SIP_UNDECIPHERABLE = 378;
+
+ /**
* MEDIA (IMS -> Telephony)
*/
/**
@@ -384,6 +457,24 @@
* The call has been terminated by the network or remote user.
*/
public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+ /**
+ * Upgrade Downgrade request rejected by
+ * Remote user if the request is MO initiated
+ * Local user if the request is MT initiated
+ */
+ public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
+
+ /**
+ * Upgrade Downgrade request cacncelled by the user who initiated it
+ */
+ public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
+
+ /**
+ * UPGRADE DOWNGRADE operation failed
+ * This can happen due to failure from SIP/RTP/SDP generation or a Call end is
+ * triggered/received while Reinvite is in progress.
+ */
+ public static final int CODE_SESSION_MODIFICATION_FAILED = 1517;
/*
* UT
@@ -484,6 +575,16 @@
public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
/**
+ * For MultiEndPoint - Call was rejected elsewhere
+ */
+ public static final int CODE_REJECTED_ELSEWHERE = 1017;
+
+ /**
+ * Supplementary services (HOLD/RESUME) failure error codes.
+ * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ */
+
+ /**
* Supplementary Services (HOLD/RESUME) - the command failed.
*/
public static final int CODE_SUPP_SVC_FAILED = 1201;
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
new file mode 100644
index 0000000..d50b516
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * The manager class for RCS related utilities.
+ * @hide
+ */
+@SystemService(Context.TELEPHONY_RCS_SERVICE)
+public class RcsManager {
+
+ private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore();
+
+ /**
+ * Returns an instance of RcsMessageStore.
+ */
+ public RcsMessageStore getRcsMessageStore() {
+ return sRcsMessageStoreInstance;
+ }
+}
diff --git a/telephony/java/android/telephony/rcs/RcsManager.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
similarity index 78%
rename from telephony/java/android/telephony/rcs/RcsManager.java
rename to telephony/java/android/telephony/ims/RcsMessageStore.java
index 0ef4e15..c89c0be 100644
--- a/telephony/java/android/telephony/rcs/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -14,24 +14,20 @@
* limitations under the License.
*/
-package android.telephony.rcs;
+package android.telephony.ims;
-import android.annotation.SystemService;
-import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.Rlog;
-
-import com.android.internal.telephony.rcs.IRcs;
+import android.telephony.ims.aidl.IRcs;
/**
- * RcsManager is the application interface to RcsProvider and provides access methods to
+ * RcsMessageStore is the application interface to RcsProvider and provides access methods to
* RCS related database tables.
* @hide - TODO make this public
*/
-@SystemService(Context.TELEPHONY_RCS_SERVICE)
-public class RcsManager {
- private static final String TAG = "RcsManager";
+public class RcsMessageStore {
+ private static final String TAG = "RcsMessageStore";
private static final boolean VDBG = false;
/**
diff --git a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
similarity index 63%
copy from telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
copy to telephony/java/android/telephony/ims/RcsThread.aidl
index 4c289ac..79d47326 100644
--- a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/RcsThread.aidl
@@ -1,11 +1,12 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Copyright 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,12 +15,6 @@
* limitations under the License.
*/
-package com.android.internal.telephony.rcs;
+package android.telephony;
-interface IRcs {
- // RcsManager APIs
- void deleteThread(int threadId);
-
- // RcsThread APIs
- int getMessageCount(int rcsThreadId);
-}
\ No newline at end of file
+parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/rcs/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
similarity index 96%
rename from telephony/java/android/telephony/rcs/RcsThread.java
rename to telephony/java/android/telephony/ims/RcsThread.java
index 83eb973..b7f440d 100644
--- a/telephony/java/android/telephony/rcs/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package android.telephony.rcs;
+package android.telephony.ims;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-
-import com.android.internal.telephony.rcs.IRcs;
+import android.telephony.ims.aidl.IRcs;
/**
* RcsThread represents a single RCS conversation thread. It holds messages that were sent and
diff --git a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
similarity index 83%
rename from telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
rename to telephony/java/android/telephony/ims/aidl/IRcs.aidl
index 4c289ac..b2e2fad 100644
--- a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.telephony.rcs;
+package android.telephony.ims.aidl;
+/**
+ * RPC definition between RCS storage APIs and phone process.
+ * {@hide}
+ */
interface IRcs {
- // RcsManager APIs
+ // RcsMessageStore APIs
void deleteThread(int threadId);
// RcsThread APIs
diff --git a/telephony/java/android/telephony/rcs/RcsThread.aidl b/telephony/java/android/telephony/rcs/RcsThread.aidl
deleted file mode 100644
index e2e0da5d..0000000
--- a/telephony/java/android/telephony/rcs/RcsThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.telephony;
-
-parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index c934317..2ebe870 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -421,6 +421,7 @@
int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 202;
int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204;
+ int RIL_REQUEST_EMERGENCY_DIAL = 205;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index eed8ae7..5ea8ff1 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -39,7 +39,6 @@
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -840,6 +839,7 @@
/* SAMPLE OUTPUT : Cold launch
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Status: ok
+ LaunchState: COLD
Activity: com.google.android.calculator/com.android.calculator2.Calculator
TotalTime: 357
WaitTime: 377
@@ -848,6 +848,7 @@
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Warning: Activity not started, its current task has been brought to the front
Status: ok
+ LaunchState: HOT
Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
TotalTime: 60
WaitTime: 67
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
index d8b3b20..75ee089 100644
--- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
+++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
@@ -18,20 +18,23 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.UiAutomation;
import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.util.EventLog;
+
import dalvik.system.DexClassLoader;
-import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -40,6 +43,7 @@
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* Integration tests for {@link com.android.server.pm.dex.DexLogger}.
@@ -47,10 +51,10 @@
* The setup for the test dynamically loads code in a jar extracted
* from our assets (a secondary dex file).
*
- * We then use adb to trigger secondary dex file reconcilation (and
- * wait for it to complete). As a side-effect of this DexLogger should
- * be notified of the file and should log the hash of the file's name
- * and content. We verify that this message appears in the event log.
+ * We then use shell commands to trigger dynamic code logging (and wait
+ * for it to complete). This causes DexLogger to log the hash of the
+ * file's name and content. We verify that this message appears in
+ * the event log.
*
* Run with "atest DexLoggerIntegrationTests".
*/
@@ -58,29 +62,89 @@
@RunWith(JUnit4.class)
public final class DexLoggerIntegrationTests {
- private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest";
-
// Event log tag used for SNET related events
private static final int SNET_TAG = 0x534e4554;
+
// Subtag used to distinguish dynamic code loading events
private static final String DCL_SUBTAG = "dcl";
- // Obtained via "echo -n copied.jar | sha256sum"
- private static final String EXPECTED_NAME_HASH =
- "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
+ // All the tags we care about
+ private static final int[] TAG_LIST = new int[] { SNET_TAG };
- private static String expectedContentHash;
+ // This is {@code DynamicCodeLoggingService#JOB_ID}
+ private static final int DYNAMIC_CODE_LOGGING_JOB_ID = 2030028;
+
+ private static Context sContext;
+ private static int sMyUid;
@BeforeClass
- public static void setUpAll() throws Exception {
- Context context = InstrumentationRegistry.getTargetContext();
+ public static void setUpAll() {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sMyUid = android.os.Process.myUid();
+ }
+
+ @Before
+ public void primeEventLog() {
+ // Force a round trip to logd to make sure everything is up to date.
+ // Without this the first test passes and others don't - we don't see new events in the
+ // log. The exact reason is unclear.
+ EventLog.writeEvent(SNET_TAG, "Dummy event");
+ }
+
+ @Test
+ public void testDexLoggerGeneratesEvents() throws Exception {
+ File privateCopyFile = fileForJar("copied.jar");
+ // Obtained via "echo -n copied.jar | sha256sum"
+ String expectedNameHash =
+ "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
+ String expectedContentHash = copyAndHashJar(privateCopyFile);
+
+ // Feed the jar to a class loader and make sure it contains what we expect.
+ ClassLoader parentClassLoader = sContext.getClass().getClassLoader();
+ ClassLoader loader =
+ new DexClassLoader(privateCopyFile.toString(), null, null, parentClassLoader);
+ loader.loadClass("com.android.dcl.Simple");
+
+ // And make sure we log events about it
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDexLogger();
+
+ assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+ }
+
+ @Test
+
+ public void testDexLoggerGeneratesEvents_unknownClassLoader() throws Exception {
+ File privateCopyFile = fileForJar("copied2.jar");
+ String expectedNameHash =
+ "202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
+ String expectedContentHash = copyAndHashJar(privateCopyFile);
+
+ // This time make sure an unknown class loader is an ancestor of the class loader we use.
+ ClassLoader knownClassLoader = sContext.getClass().getClassLoader();
+ ClassLoader unknownClassLoader = new UnknownClassLoader(knownClassLoader);
+ ClassLoader loader =
+ new DexClassLoader(privateCopyFile.toString(), null, null, unknownClassLoader);
+ loader.loadClass("com.android.dcl.Simple");
+
+ // And make sure we log events about it
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDexLogger();
+
+ assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+ }
+
+ private static File fileForJar(String name) {
+ return new File(sContext.getDir("jars", Context.MODE_PRIVATE), name);
+ }
+
+ private static String copyAndHashJar(File copyTo) throws Exception {
MessageDigest hasher = MessageDigest.getInstance("SHA-256");
// Copy the jar from our Java resources to a private data directory
- File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar");
Class<?> thisClass = DexLoggerIntegrationTests.class;
try (InputStream input = thisClass.getResourceAsStream("/javalib.jar");
- OutputStream output = new FileOutputStream(privateCopy)) {
+ OutputStream output = new FileOutputStream(copyTo)) {
byte[] buffer = new byte[1024];
while (true) {
int numRead = input.read(buffer);
@@ -92,42 +156,63 @@
}
}
- // Remember the SHA-256 of the file content to check that it is the same as
- // the value we see logged.
+ // Compute the SHA-256 of the file content so we can check that it is the same as the value
+ // we see logged.
Formatter formatter = new Formatter();
for (byte b : hasher.digest()) {
formatter.format("%02X", b);
}
- expectedContentHash = formatter.toString();
- // Feed the jar to a class loader and make sure it contains what we expect.
- ClassLoader loader =
- new DexClassLoader(
- privateCopy.toString(), null, null, context.getClass().getClassLoader());
- loader.loadClass("com.android.dcl.Simple");
+ return formatter.toString();
}
- @Test
- public void testDexLoggerReconcileGeneratesEvents() throws Exception {
- int[] tagList = new int[] { SNET_TAG };
+ private static long mostRecentEventTimeNanos() throws Exception {
List<EventLog.Event> events = new ArrayList<>();
- // There may already be events in the event log - figure out the most recent one
- EventLog.readEvents(tagList, events);
- long previousEventNanos =
- events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
- events.clear();
+ EventLog.readEvents(TAG_LIST, events);
+ return events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
+ }
- Process process = Runtime.getRuntime().exec(
- "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME);
- int exitCode = process.waitFor();
- assertThat(exitCode).isEqualTo(0);
+ private static void runDexLogger() throws Exception {
+ // This forces {@code DynamicCodeLoggingService} to start now.
+ runCommand("cmd jobscheduler run -f android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+ // Wait for the job to have run.
+ long startTime = SystemClock.elapsedRealtime();
+ while (true) {
+ String response = runCommand(
+ "cmd jobscheduler get-job-state android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+ if (!response.contains("pending") && !response.contains("active")) {
+ break;
+ }
+ if (SystemClock.elapsedRealtime() - startTime > TimeUnit.SECONDS.toMillis(10)) {
+ throw new AssertionError("Job has not completed: " + response);
+ }
+ SystemClock.sleep(100);
+ }
+ }
- int myUid = android.os.Process.myUid();
- String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash;
+ private static String runCommand(String command) throws Exception {
+ ByteArrayOutputStream response = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1000];
+ UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ ParcelFileDescriptor fd = ui.executeShellCommand(command);
+ try (InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+ while (true) {
+ int count = input.read(buffer);
+ if (count == -1) {
+ break;
+ }
+ response.write(buffer, 0, count);
+ }
+ }
+ return response.toString("UTF-8");
+ }
- EventLog.readEvents(tagList, events);
- boolean found = false;
+ private static void assertDclLoggedSince(long previousEventNanos, String expectedNameHash,
+ String expectedContentHash) throws Exception {
+ List<EventLog.Event> events = new ArrayList<>();
+ EventLog.readEvents(TAG_LIST, events);
+ int found = 0;
for (EventLog.Event event : events) {
if (event.getTimeNanos() <= previousEventNanos) {
continue;
@@ -140,15 +225,28 @@
continue;
}
int uid = (int) data[1];
- if (uid != myUid) {
+ if (uid != sMyUid) {
continue;
}
String message = (String) data[2];
- assertThat(message).isEqualTo(expectedMessage);
- found = true;
+ if (!message.startsWith(expectedNameHash)) {
+ continue;
+ }
+
+ assertThat(message).endsWith(expectedContentHash);
+ ++found;
}
- assertThat(found).isTrue();
+ assertThat(found).isEqualTo(1);
+ }
+
+ /**
+ * A class loader that does nothing useful, but importantly doesn't extend BaseDexClassLoader.
+ */
+ private static class UnknownClassLoader extends ClassLoader {
+ UnknownClassLoader(ClassLoader parent) {
+ super(parent);
+ }
}
}
diff --git a/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java b/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
similarity index 83%
rename from tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java
rename to tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
index 7f5f03e0d..290e04c 100644
--- a/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java
+++ b/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
@@ -16,17 +16,17 @@
package com.android.tests.rcs;
import android.support.test.runner.AndroidJUnit4;
-import android.telephony.rcs.RcsManager;
+import android.telephony.ims.RcsMessageStore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-public class RcsManagerTest {
+public class RcsMessageStoreTest {
//TODO(sahinc): Add meaningful tests once we have more of the implementation in place
@Test
public void testDeleteThreadDoesntCrash() {
- RcsManager mRcsManager = new RcsManager();
- mRcsManager.deleteThread(0);
+ RcsMessageStore mRcsMessageStore = new RcsMessageStore();
+ mRcsMessageStore.deleteThread(0);
}
}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 9838020..151b559 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -46,6 +46,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.text.format.DateUtils;
+import android.util.Log;
import com.android.frameworks.tests.net.R;
import com.android.internal.util.HexDump;
import java.io.File;
@@ -89,6 +90,7 @@
System.loadLibrary("frameworksnettestsjni");
}
+ private static final String TAG = "ApfTest";
// Expected return codes from APF interpreter.
private static final int PASS = 1;
private static final int DROP = 0;
@@ -869,6 +871,37 @@
}
}
+ /**
+ * Generate APF program, run pcap file though APF filter, then check all the packets in the file
+ * should be dropped.
+ */
+ @Test
+ public void testApfFilterPcapFile() throws Exception {
+ final byte[] MOCK_PCAP_IPV4_ADDR = {(byte) 172, 16, 7, (byte) 151};
+ String pcapFilename = stageFile(R.raw.apfPcap);
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+ LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_PCAP_IPV4_ADDR), 16);
+ LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(link);
+
+ ApfConfiguration config = getDefaultConfig();
+ ApfCapabilities MOCK_APF_PCAP_CAPABILITIES = new ApfCapabilities(4, 1700, ARPHRD_ETHER);
+ config.apfCapabilities = MOCK_APF_PCAP_CAPABILITIES;
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ apfFilter.setLinkProperties(lp);
+ byte[] program = ipClientCallback.getApfProgram();
+ byte[] data = new byte[ApfFilter.Counter.totalSize()];
+ final boolean result;
+
+ result = dropsAllPackets(program, data, pcapFilename);
+ Log.i(TAG, "testApfFilterPcapFile(): Data counters: " + HexDump.toHexString(data, false));
+
+ assertTrue("Failed to drop all packets by filter. \nAPF counters:" +
+ HexDump.toHexString(data, false), result);
+ }
+
private class MockIpClientCallback extends IpClient.Callback {
private final ConditionVariable mGotApfProgram = new ConditionVariable();
private byte[] mLastApfProgram;
@@ -1015,12 +1048,17 @@
4, // Protocol size: 4
0, 2 // Opcode: reply (2)
};
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
+ private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19
private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2};
+ private static final byte[] IPV4_SOURCE_ADDR = {10, 0, 0, 3};
+ private static final byte[] ANOTHER_IPV4_SOURCE_ADDR = {(byte) 192, 0, 2, 1};
+ private static final byte[] BUG_PROBE_SOURCE_ADDR1 = {0, 0, 1, 2};
+ private static final byte[] BUG_PROBE_SOURCE_ADDR2 = {3, 4, 0, 0};
private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
// Helper to initialize a default apfFilter.
@@ -1366,10 +1404,16 @@
assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR));
assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR));
+ // Verify ARP reply packets from different source ip
+ assertDrop(program, arpReply(IPV4_ANY_HOST_ADDR, IPV4_ANY_HOST_ADDR));
+ assertPass(program, arpReply(ANOTHER_IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
+ assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR1, IPV4_ANY_HOST_ADDR));
+ assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR2, IPV4_ANY_HOST_ADDR));
+
// Verify unicast ARP reply packet is always accepted.
- assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR));
- assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR));
- assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR));
+ assertPass(program, arpReply(IPV4_SOURCE_ADDR, MOCK_IPV4_ADDR));
+ assertPass(program, arpReply(IPV4_SOURCE_ADDR, ANOTHER_IPV4_ADDR));
+ assertPass(program, arpReply(IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
// Verify GARP reply packets are always filtered
assertDrop(program, garpReply());
@@ -1398,19 +1442,20 @@
apfFilter.shutdown();
}
- private static byte[] arpRequestBroadcast(byte[] tip) {
+ private static byte[] arpReply(byte[] sip, byte[] tip) {
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+ put(packet, ARP_SOURCE_IP_ADDRESS_OFFSET, sip);
put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
return packet.array();
}
- private static byte[] arpReplyUnicast(byte[] tip) {
+ private static byte[] arpRequestBroadcast(byte[] tip) {
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+ put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+ put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REQUEST_HEADER);
put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
return packet.array();
}
@@ -1706,6 +1751,14 @@
private native static boolean compareBpfApf(String filter, String pcap_filename,
byte[] apf_program);
+
+ /**
+ * Open packet capture file {@code pcapFilename} and run it through APF filter. Then
+ * checks whether all the packets are dropped and populates data[] {@code data} with
+ * the APF counters.
+ */
+ private native static boolean dropsAllPackets(byte[] program, byte[] data, String pcapFilename);
+
@Test
public void testBroadcastAddress() throws Exception {
assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8081812..bca9be7 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -71,7 +71,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
@@ -130,10 +129,6 @@
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
- // Actual contents of the request don't matter for this test. The lack of
- // any specific TRANSPORT_* is sufficient to identify this request.
- private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
-
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
@@ -257,11 +252,6 @@
isTetheringSupportedCalls++;
return true;
}
-
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- return mDefaultRequest;
- }
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
@@ -496,7 +486,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
- verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
+ verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
@@ -730,7 +720,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
- verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
+ verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index a22cbd4..0afd607 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -24,6 +24,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -53,7 +54,6 @@
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.util.SharedLog;
-
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -65,7 +65,6 @@
import org.junit.runner.RunWith;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -126,7 +125,7 @@
}
@Test
- public void testDoesNothingBeforeStarted() {
+ public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception {
assertTrue(mCM.hasNoCallbacks());
assertFalse(mUNM.mobileNetworkRequested());
@@ -138,37 +137,40 @@
@Test
public void testDefaultNetworkIsTracked() throws Exception {
- assertEquals(0, mCM.trackingDefault.size());
+ assertTrue(mCM.hasNoCallbacks());
+ mUNM.startTrackDefaultNetwork(mDefaultRequest);
- mUNM.start(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
assertEquals(1, mCM.trackingDefault.size());
mUNM.stop();
- assertTrue(mCM.hasNoCallbacks());
+ assertTrue(mCM.onlyHasDefaultCallbacks());
}
@Test
public void testListensForAllNetworks() throws Exception {
assertTrue(mCM.listening.isEmpty());
- mUNM.start(mDefaultRequest);
+ mUNM.startTrackDefaultNetwork(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
assertFalse(mCM.listening.isEmpty());
assertTrue(mCM.isListeningForAll());
mUNM.stop();
- assertTrue(mCM.hasNoCallbacks());
+ assertTrue(mCM.onlyHasDefaultCallbacks());
}
@Test
public void testCallbacksRegistered() {
- mUNM.start(mDefaultRequest);
- verify(mCM, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+ mUNM.startTrackDefaultNetwork(mDefaultRequest);
verify(mCM, times(1)).requestNetwork(
eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
+ mUNM.startObserveAllNetworks();
+ verify(mCM, times(1)).registerNetworkCallback(
+ any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
mUNM.stop();
- verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
+ verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
}
@Test
@@ -176,7 +178,7 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -199,11 +201,9 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
verify(mCM, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mCM, times(1)).requestNetwork(
- eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -227,7 +227,7 @@
assertTrue(mCM.isDunRequested());
mUNM.stop();
- verify(mCM, times(3)).unregisterNetworkCallback(any(NetworkCallback.class));
+ verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
verifyNoMoreInteractions(mCM);
}
@@ -237,7 +237,7 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -257,7 +257,7 @@
@Test
public void testUpdateMobileRequiresDun() throws Exception {
- mUNM.start(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
// Test going from no-DUN to DUN correctly re-registers callbacks.
mUNM.updateMobileRequiresDun(false);
@@ -285,7 +285,8 @@
final Collection<Integer> preferredTypes = new ArrayList<>();
preferredTypes.add(TYPE_WIFI);
- mUNM.start(mDefaultRequest);
+ mUNM.startTrackDefaultNetwork(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -350,7 +351,8 @@
@Test
public void testGetCurrentPreferredUpstream() throws Exception {
- mUNM.start(mDefaultRequest);
+ mUNM.startTrackDefaultNetwork(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
mUNM.updateMobileRequiresDun(false);
// [0] Mobile connects, DUN not required -> mobile selected.
@@ -389,7 +391,8 @@
@Test
public void testLocalPrefixes() throws Exception {
- mUNM.start(mDefaultRequest);
+ mUNM.startTrackDefaultNetwork(mDefaultRequest);
+ mUNM.startObserveAllNetworks();
// [0] Test minimum set of local prefixes.
Set<IpPrefix> local = mUNM.getLocalPrefixes();
@@ -521,11 +524,19 @@
}
boolean hasNoCallbacks() {
- return allCallbacks.isEmpty() &&
- trackingDefault.isEmpty() &&
- listening.isEmpty() &&
- requested.isEmpty() &&
- legacyTypeMap.isEmpty();
+ return allCallbacks.isEmpty()
+ && trackingDefault.isEmpty()
+ && listening.isEmpty()
+ && requested.isEmpty()
+ && legacyTypeMap.isEmpty();
+ }
+
+ boolean onlyHasDefaultCallbacks() {
+ return (allCallbacks.size() == 1)
+ && (trackingDefault.size() == 1)
+ && listening.isEmpty()
+ && requested.isEmpty()
+ && legacyTypeMap.isEmpty();
}
boolean isListeningForAll() {
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index 1ea9e27..4222adf 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -21,37 +21,40 @@
#include <stdlib.h>
#include <string>
#include <utils/Log.h>
+#include <vector>
#include "apf_interpreter.h"
+#include "nativehelper/scoped_primitive_array.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
// JNI function acting as simply call-through to native APF interpreter.
static jint com_android_server_ApfTest_apfSimulate(
- JNIEnv* env, jclass, jbyteArray program, jbyteArray packet,
- jbyteArray data, jint filter_age) {
- uint8_t* program_raw = (uint8_t*)env->GetByteArrayElements(program, nullptr);
- uint8_t* packet_raw = (uint8_t*)env->GetByteArrayElements(packet, nullptr);
- uint8_t* data_raw = (uint8_t*)(data ? env->GetByteArrayElements(data, nullptr) : nullptr);
- uint32_t program_len = env->GetArrayLength(program);
- uint32_t packet_len = env->GetArrayLength(packet);
- uint32_t data_len = data ? env->GetArrayLength(data) : 0;
+ JNIEnv* env, jclass, jbyteArray jprogram, jbyteArray jpacket,
+ jbyteArray jdata, jint filter_age) {
- // Merge program and data into a single buffer.
- uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len);
- memcpy(program_and_data, program_raw, program_len);
- memcpy(program_and_data + program_len, data_raw, data_len);
+ ScopedByteArrayRO packet(env, jpacket);
+ uint32_t packet_len = (uint32_t)packet.size();
+ uint32_t program_len = env->GetArrayLength(jprogram);
+ uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0;
+ std::vector<uint8_t> buf(program_len + data_len, 0);
+
+ env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data()));
+ if (jdata) {
+ // Merge program and data into a single buffer.
+ env->GetByteArrayRegion(jdata, 0, data_len,
+ reinterpret_cast<jbyte*>(buf.data() + program_len));
+ }
jint result =
- accept_packet(program_and_data, program_len, program_len + data_len,
- packet_raw, packet_len, filter_age);
- if (data) {
- memcpy(data_raw, program_and_data + program_len, data_len);
- env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */);
+ accept_packet(buf.data(), program_len, program_len + data_len,
+ reinterpret_cast<const uint8_t*>(packet.get()), packet_len, filter_age);
+
+ if (jdata) {
+ env->SetByteArrayRegion(jdata, 0, data_len,
+ reinterpret_cast<jbyte*>(buf.data() + program_len));
}
- free(program_and_data);
- env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT);
- env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT);
+
return result;
}
@@ -118,8 +121,7 @@
jstring jpcap_filename, jbyteArray japf_program) {
ScopedUtfChars filter(env, jfilter);
ScopedUtfChars pcap_filename(env, jpcap_filename);
- uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
- uint32_t apf_program_len = env->GetArrayLength(japf_program);
+ ScopedByteArrayRO apf_program(env, japf_program);
// Open pcap file for BPF filtering
ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
@@ -161,14 +163,15 @@
do {
apf_packet = pcap_next(apf_pcap.get(), &apf_header);
} while (apf_packet != NULL && !accept_packet(
- apf_program, apf_program_len, 0 /* data_len */,
+ reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())),
+ apf_program.size(), 0 /* data_len */,
apf_packet, apf_header.len, 0 /* filter_age */));
// Make sure both filters matched the same packet.
if (apf_packet == NULL && bpf_packet == NULL)
- break;
+ break;
if (apf_packet == NULL || bpf_packet == NULL)
- return false;
+ return false;
if (apf_header.len != bpf_header.len ||
apf_header.ts.tv_sec != bpf_header.ts.tv_sec ||
apf_header.ts.tv_usec != bpf_header.ts.tv_usec ||
@@ -178,6 +181,48 @@
return true;
}
+static jboolean com_android_server_ApfTest_dropsAllPackets(JNIEnv* env, jclass, jbyteArray jprogram,
+ jbyteArray jdata, jstring jpcap_filename) {
+ ScopedUtfChars pcap_filename(env, jpcap_filename);
+ ScopedByteArrayRO apf_program(env, jprogram);
+ uint32_t apf_program_len = (uint32_t)apf_program.size();
+ uint32_t data_len = env->GetArrayLength(jdata);
+ pcap_pkthdr apf_header;
+ const uint8_t* apf_packet;
+ char pcap_error[PCAP_ERRBUF_SIZE];
+ std::vector<uint8_t> buf(apf_program_len + data_len, 0);
+
+ // Merge program and data into a single buffer.
+ env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data()));
+ env->GetByteArrayRegion(jdata, 0, data_len,
+ reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
+
+ // Open pcap file
+ ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
+ ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
+
+ if (apf_pcap.get() == NULL) {
+ throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
+ return false;
+ }
+
+ while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) {
+ int result = accept_packet(buf.data(), apf_program_len,
+ apf_program_len + data_len, apf_packet, apf_header.len, 0);
+
+ // Return false once packet passes the filter
+ if (result) {
+ env->SetByteArrayRegion(jdata, 0, data_len,
+ reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
+ return false;
+ }
+ }
+
+ env->SetByteArrayRegion(jdata, 0, data_len,
+ reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
+ return true;
+}
+
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -192,6 +237,8 @@
(void*)com_android_server_ApfTest_compileToBpf },
{ "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z",
(void*)com_android_server_ApfTest_compareBpfApf },
+ { "dropsAllPackets", "([B[BLjava/lang/String;)Z",
+ (void*)com_android_server_ApfTest_dropsAllPackets },
};
jniRegisterNativeMethods(env, "android/net/apf/ApfTest",
diff --git a/tests/net/res/raw/apfPcap.pcap b/tests/net/res/raw/apfPcap.pcap
new file mode 100644
index 0000000..6f69c4a
--- /dev/null
+++ b/tests/net/res/raw/apfPcap.pcap
Binary files differ
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index cb8fef9..b5a990e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -183,6 +183,11 @@
self.name = self.fullname[self.fullname.rindex(".")+1:]
+ def merge_from(self, other):
+ self.ctors.extend(other.ctors)
+ self.fields.extend(other.fields)
+ self.methods.extend(other.methods)
+
def __hash__(self):
return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
@@ -204,9 +209,28 @@
return self.raw
-def _parse_stream(f, clazz_cb=None):
- line = 0
+def _parse_stream(f, clazz_cb=None, base_f=None):
api = {}
+
+ if base_f:
+ base_classes = _parse_stream_to_generator(base_f)
+ else:
+ base_classes = []
+
+ for clazz in _parse_stream_to_generator(f):
+ base_class = _parse_to_matching_class(base_classes, clazz)
+ if base_class:
+ clazz.merge_from(base_class)
+
+ if clazz_cb:
+ clazz_cb(clazz)
+ else: # In callback mode, don't keep track of the full API
+ api[clazz.fullname] = clazz
+
+ return api
+
+def _parse_stream_to_generator(f):
+ line = 0
pkg = None
clazz = None
blame = None
@@ -225,26 +249,41 @@
if raw.startswith("package"):
pkg = Package(line, raw, blame)
elif raw.startswith(" ") and raw.endswith("{"):
- # When provided with class callback, we treat as incremental
- # parse and don't build up entire API
- if clazz and clazz_cb:
- clazz_cb(clazz)
clazz = Class(pkg, line, raw, blame)
- if not clazz_cb:
- api[clazz.fullname] = clazz
elif raw.startswith(" ctor"):
clazz.ctors.append(Method(clazz, line, raw, blame))
elif raw.startswith(" method"):
clazz.methods.append(Method(clazz, line, raw, blame))
elif raw.startswith(" field"):
clazz.fields.append(Field(clazz, line, raw, blame))
+ elif raw.startswith(" }") and clazz:
+ while True:
+ retry = yield clazz
+ if not retry:
+ break
+ # send() was called, asking us to redeliver clazz on next(). Still need to yield
+ # a dummy value to the send() first though.
+ if (yield "Returning clazz on next()"):
+ raise TypeError("send() must be followed by next(), not send()")
- # Handle last trailing class
- if clazz and clazz_cb:
- clazz_cb(clazz)
- return api
+def _parse_to_matching_class(classes, needle):
+ """Takes a classes generator and parses it until it returns the class we're looking for
+ This relies on classes being sorted by package and class name."""
+
+ for clazz in classes:
+ if clazz.pkg.name < needle.pkg.name:
+ # We haven't reached the right package yet
+ continue
+ if clazz.name < needle.name:
+ # We haven't reached the right class yet
+ continue
+ if clazz.fullname == needle.fullname:
+ return clazz
+ # We ran past the right class. Send it back into the generator, then report failure.
+ classes.send(clazz)
+ return None
class Failure():
def __init__(self, sig, clazz, detail, error, rule, msg):
@@ -1504,12 +1543,12 @@
verify_singleton(clazz)
-def examine_stream(stream):
+def examine_stream(stream, base_stream=None):
"""Find all style issues in the given API stream."""
global failures, noticed
failures = {}
noticed = {}
- _parse_stream(stream, examine_clazz)
+ _parse_stream(stream, examine_clazz, base_f=base_stream)
return (failures, noticed)
@@ -1650,6 +1689,12 @@
parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
help="previous.txt")
+ parser.add_argument("--base-current", nargs='?', type=argparse.FileType('r'), default=None,
+ help="The base current.txt to use when examining system-current.txt or"
+ " test-current.txt")
+ parser.add_argument("--base-previous", nargs='?', type=argparse.FileType('r'), default=None,
+ help="The base previous.txt to use when examining system-previous.txt or"
+ " test-previous.txt")
parser.add_argument("--no-color", action='store_const', const=True,
help="Disable terminal colors")
parser.add_argument("--allow-google", action='store_const', const=True,
@@ -1669,7 +1714,9 @@
ALLOW_GOOGLE = True
current_file = args['current.txt']
+ base_current_file = args['base_current']
previous_file = args['previous.txt']
+ base_previous_file = args['base_previous']
if args['show_deprecations_at_birth']:
with current_file as f:
@@ -1688,10 +1735,18 @@
sys.exit()
with current_file as f:
- cur_fail, cur_noticed = examine_stream(f)
+ if base_current_file:
+ with base_current_file as base_f:
+ cur_fail, cur_noticed = examine_stream(f, base_f)
+ else:
+ cur_fail, cur_noticed = examine_stream(f)
if not previous_file is None:
with previous_file as f:
- prev_fail, prev_noticed = examine_stream(f)
+ if base_previous_file:
+ with base_previous_file as base_f:
+ prev_fail, prev_noticed = examine_stream(f, base_f)
+ else:
+ prev_fail, prev_noticed = examine_stream(f)
# ignore errors from previous API level
for p in prev_fail: