Merge "Fix style error"
diff --git a/Android.bp b/Android.bp
index 32bd408..5a1b813 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",
@@ -1190,6 +1190,7 @@
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
"--hide-package com.android.okhttp " +
"--hide-package com.android.org.conscrypt --hide-package com.android.server " +
+ "--error UnhiddenSystemApi " +
"--hide RequiresPermission " +
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
@@ -1606,6 +1607,7 @@
dex_mapping_filename: "dex-mapping.txt",
args: metalava_framework_docs_args +
" --hide ReferencesHidden " +
+ " --hide UnhiddenSystemApi " +
" --show-unannotated " +
" --show-annotation android.annotation.SystemApi " +
" --show-annotation android.annotation.TestApi "
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 f1f3789..6e0ca5d 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";
@@ -940,6 +940,7 @@
field public static final int menuCategory = 16843230; // 0x10101de
field public static final int mimeType = 16842790; // 0x1010026
field public static final int min = 16844089; // 0x1010539
+ field public static final int minAspectRatio = 16844193; // 0x10105a1
field public static final int minDate = 16843583; // 0x101033f
field public static final int minEms = 16843098; // 0x101015a
field public static final int minHeight = 16843072; // 0x1010140
@@ -11227,6 +11228,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 +11453,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 +11461,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 +13573,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 +13675,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 +14298,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 +14353,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 +14373,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 +14407,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 +14689,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 +15245,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);
@@ -16529,6 +16584,7 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> REQUEST_PIPELINE_MAX_DEPTH;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN;
@@ -17099,6 +17155,18 @@
field public static final float MINIMUM_GAIN_FACTOR = 1.0f;
}
+ public final class MandatoryStreamCombination {
+ method public java.lang.String getDescription();
+ method public java.util.List<android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation> getStreamsInformation();
+ method public boolean isReprocessable();
+ }
+
+ public static final class MandatoryStreamCombination.MandatoryStreamInformation {
+ method public java.util.List<android.util.Size> getAvailableSizes();
+ method public int getFormat();
+ method public boolean isInput();
+ }
+
public final class MeteringRectangle {
ctor public MeteringRectangle(int, int, int, int, int);
ctor public MeteringRectangle(android.graphics.Point, android.util.Size, int);
@@ -26266,9 +26334,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;
@@ -29692,6 +29763,13 @@
field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
}
+ public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
+ method public int describeContents();
+ method public java.net.Inet6Address getPeerIpv6Addr();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkInfo> CREATOR;
+ }
+
public class WifiAwareSession implements java.lang.AutoCloseable {
method public void close();
method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
@@ -36298,6 +36376,12 @@
field public static final java.lang.String CACHED_NUMBER_TYPE = "numbertype";
field public static final java.lang.String CACHED_PHOTO_ID = "photo_id";
field public static final java.lang.String CACHED_PHOTO_URI = "photo_uri";
+ field public static final java.lang.String CALL_ID_APP_NAME = "call_id_app_name";
+ field public static final java.lang.String CALL_ID_DESCRIPTION = "call_id_description";
+ field public static final java.lang.String CALL_ID_DETAILS = "call_id_details";
+ field public static final java.lang.String CALL_ID_NAME = "call_id_name";
+ field public static final java.lang.String CALL_ID_NUISANCE_CONFIDENCE = "call_id_nuisance_confidence";
+ field public static final java.lang.String CALL_ID_PACKAGE_NAME = "call_id_package_name";
field public static final java.lang.String CALL_SCREENING_APP_NAME = "call_screening_app_name";
field public static final java.lang.String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name";
field public static final android.net.Uri CONTENT_FILTER_URI;
@@ -37479,25 +37563,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";
@@ -40995,12 +41091,12 @@
public static class NotificationListenerService.Ranking {
ctor public NotificationListenerService.Ranking();
- method public boolean audiblyAlerted();
method public boolean canShowBadge();
method public android.app.NotificationChannel getChannel();
method public int getImportance();
method public java.lang.CharSequence getImportanceExplanation();
method public java.lang.String getKey();
+ method public long getLastAudiblyAlertedMillis();
method public java.lang.String getOverrideGroupKey();
method public int getRank();
method public int getSuppressedVisualEffects();
@@ -42136,7 +42232,9 @@
field public static final int SIOCGIFBRDADDR;
field public static final int SIOCGIFDSTADDR;
field public static final int SIOCGIFNETMASK;
+ field public static final int SOCK_CLOEXEC;
field public static final int SOCK_DGRAM;
+ field public static final int SOCK_NONBLOCK;
field public static final int SOCK_RAW;
field public static final int SOCK_SEQPACKET;
field public static final int SOCK_STREAM;
@@ -42443,6 +42541,7 @@
method public static java.lang.String capabilitiesToString(int);
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public int getCallCapabilities();
+ method public android.telecom.CallIdentification getCallIdentification();
method public int getCallProperties();
method public java.lang.String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
@@ -42521,6 +42620,34 @@
field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
+ public final class CallIdentification implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getCallScreeningAppName();
+ method public java.lang.String getCallScreeningPackageName();
+ method public java.lang.String getDescription();
+ method public java.lang.String getDetails();
+ method public java.lang.String getName();
+ method public int getNuisanceConfidence();
+ method public android.graphics.drawable.Icon getPhoto();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CONFIDENCE_LIKELY_NOT_NUISANCE = -1; // 0xffffffff
+ field public static final int CONFIDENCE_LIKELY_NUISANCE = 1; // 0x1
+ field public static final int CONFIDENCE_NOT_NUISANCE = -2; // 0xfffffffe
+ field public static final int CONFIDENCE_NUISANCE = 2; // 0x2
+ field public static final int CONFIDENCE_UNKNOWN = 0; // 0x0
+ field public static final android.os.Parcelable.Creator<android.telecom.CallIdentification> CREATOR;
+ }
+
+ public static class CallIdentification.Builder {
+ ctor public CallIdentification.Builder();
+ method public android.telecom.CallIdentification build();
+ method public android.telecom.CallIdentification.Builder setDescription(java.lang.String);
+ method public android.telecom.CallIdentification.Builder setDetails(java.lang.String);
+ method public android.telecom.CallIdentification.Builder setName(java.lang.String);
+ method public android.telecom.CallIdentification.Builder setNuisanceConfidence(int);
+ method public android.telecom.CallIdentification.Builder setPhoto(android.graphics.drawable.Icon);
+ }
+
public abstract class CallRedirectionService extends android.app.Service {
ctor public CallRedirectionService();
method public final void cancelCall();
@@ -42536,6 +42663,7 @@
ctor public CallScreeningService();
method public android.os.IBinder onBind(android.content.Intent);
method public abstract void onScreenCall(android.telecom.Call.Details);
+ method public final void provideCallIdentification(android.telecom.Call.Details, android.telecom.CallIdentification);
method public final void respondToCall(android.telecom.Call.Details, android.telecom.CallScreeningService.CallResponse);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
}
@@ -43130,7 +43258,6 @@
method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
- method public boolean isDefaultCallScreeningApp(android.content.ComponentName);
method public boolean isInCall();
method public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -43139,7 +43266,6 @@
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
- method public void requestChangeDefaultCallScreeningApp(android.content.ComponentName);
method public void showInCallScreen(boolean);
method public void silenceRinger();
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -43168,6 +43294,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";
@@ -43175,14 +43302,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
@@ -44176,6 +44302,8 @@
method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public boolean removeSubscriptionsFromGroup(int[]);
+ method public boolean setMetered(boolean, int);
+ method public boolean setOpportunistic(boolean, int);
method public java.lang.String setSubscriptionGroup(int[]);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -48893,7 +49021,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();
@@ -52522,9 +52652,10 @@
package android.view.textclassifier {
public final class ConversationActions implements android.os.Parcelable {
- ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>);
+ ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>, java.lang.String);
method public int describeContents();
method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
+ method public java.lang.String getId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
@@ -52585,6 +52716,7 @@
method public int describeContents();
method public java.lang.String getCallingPackageName();
method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
+ method public java.lang.String getConversationId();
method public java.util.List<java.lang.String> getHints();
method public int getMaxSuggestions();
method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
@@ -52595,6 +52727,7 @@
public static final class ConversationActions.Request.Builder {
ctor public ConversationActions.Request.Builder(java.util.List<android.view.textclassifier.ConversationActions.Message>);
method public android.view.textclassifier.ConversationActions.Request build();
+ method public android.view.textclassifier.ConversationActions.Request.Builder setConversationId(java.lang.String);
method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
@@ -52757,6 +52890,7 @@
method public default int getMaxGenerateLinksTextLength();
method public default boolean isDestroyed();
method public default void onSelectionEvent(android.view.textclassifier.SelectionEvent);
+ method public default void onTextClassifierEvent(android.view.textclassifier.TextClassifierEvent);
method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
@@ -52778,6 +52912,7 @@
field public static final java.lang.String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
field public static final java.lang.String WIDGET_TYPE_EDITTEXT = "edittext";
field public static final java.lang.String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
+ field public static final java.lang.String WIDGET_TYPE_NOTIFICATION = "notification";
field public static final java.lang.String WIDGET_TYPE_TEXTVIEW = "textview";
field public static final java.lang.String WIDGET_TYPE_UNKNOWN = "unknown";
field public static final java.lang.String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
@@ -52795,6 +52930,68 @@
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
}
+ public final class TextClassifierEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public int[] getActionIndices();
+ method public java.lang.String getEntityType();
+ method public int getEventCategory();
+ method public android.view.textclassifier.TextClassificationContext getEventContext();
+ method public int getEventIndex();
+ method public long getEventTime();
+ method public int getEventType();
+ method public android.os.Bundle getExtras();
+ method public java.lang.String getLanguage();
+ method public int getRelativeSuggestedWordEndIndex();
+ method public int getRelativeSuggestedWordStartIndex();
+ method public int getRelativeWordEndIndex();
+ method public int getRelativeWordStartIndex();
+ method public java.lang.String getResultId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3
+ field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4
+ field public static final int CATEGORY_LINKIFY = 2; // 0x2
+ field public static final int CATEGORY_SELECTION = 1; // 0x1
+ field public static final int CATEGORY_UNDEFINED = 0; // 0x0
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR;
+ field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6
+ field public static final int TYPE_AUTO_SELECTION = 5; // 0x5
+ field public static final int TYPE_COPY_ACTION = 9; // 0x9
+ field public static final int TYPE_CUT_ACTION = 11; // 0xb
+ field public static final int TYPE_LINK_CLICKED = 7; // 0x7
+ field public static final int TYPE_MANUAL_REPLY = 19; // 0x13
+ field public static final int TYPE_OTHER_ACTION = 16; // 0x10
+ field public static final int TYPE_OVERTYPE = 8; // 0x8
+ field public static final int TYPE_PASTE_ACTION = 10; // 0xa
+ field public static final int TYPE_SELECTION_DESTROYED = 15; // 0xf
+ field public static final int TYPE_SELECTION_DRAG = 14; // 0xe
+ field public static final int TYPE_SELECTION_MODIFIED = 2; // 0x2
+ field public static final int TYPE_SELECTION_RESET = 18; // 0x12
+ field public static final int TYPE_SELECTION_STARTED = 1; // 0x1
+ field public static final int TYPE_SELECT_ALL = 17; // 0x11
+ field public static final int TYPE_SHARE_ACTION = 12; // 0xc
+ field public static final int TYPE_SMART_ACTION = 13; // 0xd
+ field public static final int TYPE_SMART_SELECTION_MULTI = 4; // 0x4
+ field public static final int TYPE_SMART_SELECTION_SINGLE = 3; // 0x3
+ field public static final int TYPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class TextClassifierEvent.Builder {
+ ctor public TextClassifierEvent.Builder(int, int);
+ method public android.view.textclassifier.TextClassifierEvent build();
+ method public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(int...);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setEntityType(java.lang.String);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(android.view.textclassifier.TextClassificationContext);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setLanguage(java.lang.String);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordEndIndex(int);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordStartIndex(int);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int);
+ method public android.view.textclassifier.TextClassifierEvent.Builder setResultId(java.lang.String);
+ }
+
public final class TextLanguage implements android.os.Parcelable {
method public int describeContents();
method public float getConfidenceScore(android.icu.util.ULocale);
@@ -56170,6 +56367,7 @@
method public boolean isCursorVisible();
method public boolean isElegantTextHeight();
method public boolean isFallbackLineSpacing();
+ method public final boolean isHorizontallyScrolling();
method public boolean isInputMethodTarget();
method public boolean isSingleLine();
method public boolean isSuggestionsEnabled();
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 846d9e5..b4fce5d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -205,6 +205,7 @@
}
public static final class R.dimen {
+ field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
field public static final int config_restrictedIconSize = 17104903; // 0x1050007
}
@@ -3970,6 +3971,19 @@
field public static final java.lang.String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP";
}
+ public class Binder implements android.os.IBinder {
+ method public static final long clearCallingWorkSource();
+ method public static final int getCallingWorkSourceUid();
+ method public static final void restoreCallingWorkSource(long);
+ method public static final long setCallingWorkSourceUid(int);
+ method public static void setProxyTransactListener(android.os.Binder.ProxyTransactListener);
+ }
+
+ public static abstract interface Binder.ProxyTransactListener {
+ method public abstract void onTransactEnded(java.lang.Object);
+ method public abstract java.lang.Object onTransactStarted(android.os.IBinder, int);
+ }
+
public final class ConfigUpdate {
field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
@@ -5343,9 +5357,10 @@
method public void onDestroyTextClassificationSession(android.view.textclassifier.TextClassificationSessionId);
method public void onDetectLanguage(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLanguage.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
method public abstract void onGenerateLinks(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLinks.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
- method public void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
+ method public deprecated void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
method public void onSuggestConversationActions(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.ConversationActions.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
method public abstract void onSuggestSelection(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextSelection.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
+ method public void onTextClassifierEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextClassifierEvent);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
}
@@ -5643,7 +5658,7 @@
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean isInEmergencyCall();
method public boolean isRinging();
- method public boolean setDefaultDialer(java.lang.String);
+ method public deprecated boolean setDefaultDialer(java.lang.String);
field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
@@ -5715,16 +5730,15 @@
public abstract class NetworkService extends android.app.Service {
ctor public NetworkService();
method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
- field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
}
- public class NetworkService.NetworkServiceProvider {
+ public abstract class NetworkService.NetworkServiceProvider implements java.lang.AutoCloseable {
ctor public NetworkService.NetworkServiceProvider(int);
+ method public abstract void close();
method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
method public final int getSlotId();
method public final void notifyNetworkRegistrationStateChanged();
- method protected void onDestroy();
}
public class NetworkServiceCallback {
@@ -6064,20 +6078,19 @@
public abstract class DataService extends android.app.Service {
ctor public DataService();
method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
- field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
}
- public class DataService.DataServiceProvider {
+ public abstract class DataService.DataServiceProvider implements java.lang.AutoCloseable {
ctor public DataService.DataServiceProvider(int);
+ method public abstract void close();
method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
method public void getDataCallList(android.telephony.data.DataServiceCallback);
method public final int getSlotId();
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
- method protected void onDestroy();
method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
@@ -6545,6 +6558,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
@@ -6568,26 +6582,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_TOO_MANY_HOPS = 374; // 0x176
field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
+ 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
@@ -6597,9 +6624,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 4aa60b5..9ee0786 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
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/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index f8309bc..1ae0f52 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -270,14 +270,14 @@
}
/**
+ * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
+ * you to disable / reenable the keyguard.
+ *
* @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
* and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
* instead; this allows you to seamlessly hide the keyguard as your application
* moves in and out of the foreground and does not require that any special
* permissions be requested.
- *
- * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
- * you to disable / reenable the keyguard.
*/
@Deprecated
public class KeyguardLock {
@@ -303,7 +303,7 @@
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void disableKeyguard() {
try {
- mWM.disableKeyguard(mToken, mTag);
+ mWM.disableKeyguard(mToken, mTag, mContext.getUserId());
} catch (RemoteException ex) {
}
}
@@ -322,16 +322,17 @@
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void reenableKeyguard() {
try {
- mWM.reenableKeyguard(mToken);
+ mWM.reenableKeyguard(mToken, mContext.getUserId());
} catch (RemoteException ex) {
}
}
}
/**
- * @deprecated Use {@link KeyguardDismissCallback}
* Callback passed to {@link KeyguardManager#exitKeyguardSecurely} to notify
* caller of result.
+ *
+ * @deprecated Use {@link KeyguardDismissCallback}
*/
@Deprecated
public interface OnKeyguardExitResult {
@@ -380,12 +381,6 @@
}
/**
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
- *
* Enables you to lock or unlock the keyguard. Get an instance of this class by
* calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
* This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
@@ -394,6 +389,12 @@
*
* @return A {@link KeyguardLock} handle to use to disable and reenable the
* keyguard.
+ *
+ * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
+ * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
+ * instead; this allows you to seamlessly hide the keyguard as your application
+ * moves in and out of the foreground and does not require that any special
+ * permissions be requested.
*/
@Deprecated
public KeyguardLock newKeyguardLock(String tag) {
@@ -430,13 +431,12 @@
}
/**
- * @deprecated Use {@link #isKeyguardLocked()} instead.
- *
* If keyguard screen is showing or in restricted key input mode (i.e. in
* keyguard password emergency screen). When in such mode, certain keys,
* such as the Home key and the right soft keys, don't work.
*
* @return true if in keyguard restricted input mode.
+ * @deprecated Use {@link #isKeyguardLocked()} instead.
*/
public boolean inKeyguardRestrictedInputMode() {
return isKeyguardLocked();
@@ -588,12 +588,6 @@
}
/**
- * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
- * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
- * instead; this allows you to seamlessly hide the keyguard as your application
- * moves in and out of the foreground and does not require that any special
- * permissions be requested.
- *
* Exit the keyguard securely. The use case for this api is that, after
* disabling the keyguard, your app, which was granted permission to
* disable the keyguard and show a limited amount of information deemed
@@ -606,6 +600,12 @@
* @param callback Lets you know whether the operation was successful and
* it is safe to launch anything that would normally be considered safe
* once the user has gotten past the keyguard.
+
+ * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
+ * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
+ * instead; this allows you to seamlessly hide the keyguard as your application
+ * moves in and out of the foreground and does not require that any special
+ * permissions be requested.
*/
@Deprecated
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
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/admin/DelegatedAdminReceiver.java b/core/java/android/app/admin/DelegatedAdminReceiver.java
index 0da4e7e..9605382 100644
--- a/core/java/android/app/admin/DelegatedAdminReceiver.java
+++ b/core/java/android/app/admin/DelegatedAdminReceiver.java
@@ -63,8 +63,8 @@
*
* <p> This callback is only applicable if the delegated app has
* {@link DevicePolicyManager#DELEGATION_CERT_SELECTION} capability. Additionally, it must
- * declare an intent fitler for {@link #ACTION_CHOOSE_PRIVATE_KEY_ALIAS} in the receiver's
- * manifest in order to receive this callback.
+ * declare an intent fitler for {@link DeviceAdminReceiver#ACTION_CHOOSE_PRIVATE_KEY_ALIAS}
+ * in the receiver's manifest in order to receive this callback.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
@@ -91,8 +91,8 @@
*
* <p> This callback is only applicable if the delegated app has
* {@link DevicePolicyManager#DELEGATION_NETWORK_LOGGING} capability. Additionally, it must
- * declare an intent fitler for {@link #ACTION_NETWORK_LOGS_AVAILABLE} in the receiver's
- * manifest in order to receive this callback.
+ * declare an intent fitler for {@link DeviceAdminReceiver#ACTION_NETWORK_LOGS_AVAILABLE} in the
+ * receiver's manifest in order to receive this callback.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 670f8db..03e5933 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1637,7 +1637,7 @@
* Grants access to selection of KeyChain certificates on behalf of requesting apps.
* Once granted the app will start receiving
* DelegatedAdminReceiver.onChoosePrivateKeyAlias. The caller (PO/DO) will
- * no longer receive {@link DeviceAdminReceiver#onChoosePrivateKeyAlias()}.
+ * no longer receive {@link DeviceAdminReceiver#onChoosePrivateKeyAlias}.
* There can be at most one app that has this delegation.
* If another app already had delegated certificate selection access,
* it will lose the delegation when a new app is delegated.
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/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0a4f4eb..0edb36c 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -236,6 +236,15 @@
public float maxAspectRatio;
/**
+ * Value indicating the minimum aspect ratio the activity supports.
+ * <p>
+ * 0 means unset.
+ * @See {@link android.R.attr#minAspectRatio}.
+ * @hide
+ */
+ public float minAspectRatio;
+
+ /**
* Name of the VrListenerService component to run for this activity.
* @see android.R.attr#enableVrMode
* @hide
@@ -979,6 +988,7 @@
rotationAnimation = orig.rotationAnimation;
colorMode = orig.colorMode;
maxAspectRatio = orig.maxAspectRatio;
+ minAspectRatio = orig.minAspectRatio;
}
/**
@@ -1142,6 +1152,9 @@
if (maxAspectRatio != 0) {
pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
}
+ if (minAspectRatio != 0) {
+ pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+ }
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1190,6 +1203,7 @@
dest.writeInt(rotationAnimation);
dest.writeInt(colorMode);
dest.writeFloat(maxAspectRatio);
+ dest.writeFloat(minAspectRatio);
}
/**
@@ -1309,6 +1323,7 @@
rotationAnimation = source.readInt();
colorMode = source.readInt();
maxAspectRatio = source.readFloat();
+ minAspectRatio = source.readFloat();
}
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index c361ac1..0c438af 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -712,6 +712,15 @@
*/
public float maxAspectRatio;
+ /**
+ * Value indicating the minimum aspect ratio the application supports.
+ * <p>
+ * 0 means unset.
+ * @see {@link android.R.attr#minAspectRatio}.
+ * @hide
+ */
+ public float minAspectRatio;
+
/** @removed */
@Deprecated
public String volumeUuid;
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/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 61a74de..e38b294 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -30,6 +30,7 @@
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -162,6 +163,8 @@
SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+ private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
+ private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
@@ -3673,6 +3676,7 @@
}
ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
+ ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
ai.networkSecurityConfigRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig,
@@ -3995,6 +3999,7 @@
// Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(owner);
+ setMinAspectRatio(owner);
PackageBackwardCompatibility.modifySharedLibraries(owner);
@@ -4492,6 +4497,13 @@
0 /*default*/));
}
+ if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+ && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+ == TypedValue.TYPE_FLOAT) {
+ a.setMinAspectRatio(sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+ 0 /*default*/));
+ }
+
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
@@ -4751,6 +4763,34 @@
}
/**
+ * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+ * ratio set.
+ */
+ private void setMinAspectRatio(Package owner) {
+ final float minAspectRatio;
+ if (owner.applicationInfo.minAspectRatio != 0) {
+ // Use the application max aspect ration as default if set.
+ minAspectRatio = owner.applicationInfo.minAspectRatio;
+ } else {
+ // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
+ // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
+ // except for watches which always supported 1:1.
+ minAspectRatio = owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q
+ ? 0
+ : mCallback.hasFeature(FEATURE_WATCH)
+ ? DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
+ : DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
+ }
+
+ for (Activity activity : owner.activities) {
+ if (activity.hasMinAspectRatio()) {
+ continue;
+ }
+ activity.setMinAspectRatio(minAspectRatio);
+ }
+ }
+
+ /**
* @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
* @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
* AndroidManifest.xml.
@@ -4888,6 +4928,7 @@
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
info.maxAspectRatio = target.info.maxAspectRatio;
+ info.minAspectRatio = target.info.minAspectRatio;
info.requestedVrComponent = target.info.requestedVrComponent;
info.encryptionAware = info.directBootAware = target.info.directBootAware;
@@ -7773,11 +7814,16 @@
@UnsupportedAppUsage
public final ActivityInfo info;
private boolean mHasMaxAspectRatio;
+ private boolean mHasMinAspectRatio;
private boolean hasMaxAspectRatio() {
return mHasMaxAspectRatio;
}
+ private boolean hasMinAspectRatio() {
+ return mHasMinAspectRatio;
+ }
+
// To construct custom activity which does not exist in manifest
Activity(final Package owner, final String className, final ActivityInfo info) {
super(owner, new ArrayList<>(0), className);
@@ -7813,6 +7859,22 @@
mHasMaxAspectRatio = true;
}
+ private void setMinAspectRatio(float minAspectRatio) {
+ if (info.resizeMode == RESIZE_MODE_RESIZEABLE
+ || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+ // Resizeable activities can be put in any aspect ratio.
+ return;
+ }
+
+ if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+ // Ignore any value lesser than 1.0.
+ return;
+ }
+
+ info.minAspectRatio = minAspectRatio;
+ mHasMinAspectRatio = true;
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Activity{");
@@ -7833,12 +7895,14 @@
super.writeToParcel(dest, flags);
dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
dest.writeBoolean(mHasMaxAspectRatio);
+ dest.writeBoolean(mHasMinAspectRatio);
}
private Activity(Parcel in) {
super(in);
info = in.readParcelable(Object.class.getClassLoader());
mHasMaxAspectRatio = in.readBoolean();
+ mHasMinAspectRatio = in.readBoolean();
for (ActivityIntentInfo aii : intents) {
aii.activity = this;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 740cdae..f1a4db2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -203,11 +203,13 @@
if (FEATURE_FLAG_IDMAP2) {
final String[] systemIdmapPaths =
nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
- if (systemIdmapPaths == null) {
- throw new IOException("idmap2 scan failed");
- }
- for (String idmapPath : systemIdmapPaths) {
- apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ if (systemIdmapPaths != null) {
+ for (String idmapPath : systemIdmapPaths) {
+ apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ }
+ } else {
+ Log.w(TAG, "'idmap2 --scan' failed: no static=\"true\" overlays targeting "
+ + "\"android\" will be loaded");
}
} else {
nativeVerifySystemIdmaps();
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 5e402c7..105ae68 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -24,6 +24,8 @@
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.MandatoryStreamCombination;
+import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.TypeReference;
import android.util.Rational;
@@ -2609,6 +2611,39 @@
new Key<android.hardware.camera2.params.ReprocessFormatsMap>("android.scaler.availableRecommendedInputOutputFormatsMap", android.hardware.camera2.params.ReprocessFormatsMap.class);
/**
+ * <p>An array of mandatory stream combinations generated according to the camera device
+ * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL }
+ * and {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES }.
+ * This is an app-readable conversion of the mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} based on
+ * specific device level and capabilities.
+ * Clients can use the array as a quick reference to find an appropriate camera stream
+ * combination.
+ * As per documentation, the stream combinations with given PREVIEW, RECORD and
+ * MAXIMUM resolutions and anything smaller from the list given by
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } are
+ * guaranteed to work.
+ * The mandatory stream combination array will be {@code null} in case the device is a
+ * physical camera not independently exposed in
+ * {@link android.hardware.camera2.CameraManager#getCameraIdList } or is not backward
+ * compatible.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+ @PublicKey
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 448591f..9c213f2 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -435,9 +435,13 @@
* </table><br>
* </p>
*
+ * <p>Clients can access the above mandatory stream combination tables via
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+ *
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
- * by attempting to create a session with such targets.</p>
+ * by calling {@link #isSessionConfigurationSupported} or attempting to create a session with
+ * such targets.</p>
*
* @param outputs The new set of Surfaces that should be made available as
* targets for captured image data.
@@ -619,6 +623,9 @@
* </table><br>
* </p>
*
+ * <p>Clients can access the above mandatory stream combination tables via
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+ *
* @param inputConfig The configuration for the input {@link Surface}
* @param outputs The new set of Surfaces that should be made available as
* targets for captured image data.
@@ -980,6 +987,12 @@
* It must not impact normal camera behavior in any way and must complete significantly
* faster than creating a regular or constrained capture session.</p>
*
+ * <p>Although this method is faster than creating a new capture session, it is not intended
+ * to be used for exploring the entire space of supported stream combinations. The available
+ * mandatory stream combinations
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination} are better suited for this
+ * purpose.</p>
+ *
* <p>Note that session parameters will be ignored and calls to
* {@link SessionConfiguration#setSessionParameters} are not required.</p>
*
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 44d7364..536c2e1 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -40,6 +40,9 @@
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Size;
+import android.view.Display;
+import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Arrays;
@@ -231,6 +234,30 @@
CameraManagerGlobal.get().unregisterTorchCallback(callback);
}
+ private Size getDisplaySize() {
+ Size ret = new Size(0, 0);
+
+ try {
+ WindowManager windowManager =
+ (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+
+ int width = display.getWidth();
+ int height = display.getHeight();
+
+ if (height > width) {
+ height = width;
+ width = display.getHeight();
+ }
+
+ ret = new Size(width, height);
+ } catch (Exception e) {
+ Log.e(TAG, "getDisplaySize Failed. " + e.toString());
+ }
+
+ return ret;
+ }
+
/**
* <p>Query the capabilities of a camera device. These capabilities are
* immutable for a given camera.</p>
@@ -269,6 +296,8 @@
"Camera service is currently unavailable");
}
try {
+ Size displaySize = getDisplaySize();
+
// First check isHiddenPhysicalCamera to avoid supportsCamera2ApiLocked throwing
// exception in case cameraId is a hidden physical camera.
if (!isHiddenPhysicalCamera(cameraId) && !supportsCamera2ApiLocked(cameraId)) {
@@ -280,10 +309,19 @@
CameraInfo info = cameraService.getCameraInfo(id);
- characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
+ characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info,
+ id, displaySize);
} else {
// Normal path: Get the camera characteristics directly from the camera service
CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
+ if (!isHiddenPhysicalCamera(cameraId)) {
+ try {
+ info.setCameraId(Integer.parseInt(cameraId));
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
+ }
+ }
+ info.setDisplaySize(displaySize);
characteristics = new CameraCharacteristics(info);
}
@@ -363,7 +401,8 @@
}
Log.i(TAG, "Using legacy camera HAL.");
- cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+ cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id,
+ getDisplaySize());
}
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index f81bd13..c527ab4 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -51,6 +51,8 @@
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.MandatoryStreamCombination;
+import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.RecommendedStreamConfiguration;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
@@ -75,6 +77,7 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -577,6 +580,15 @@
}
});
sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatoryStreamCombinations();
+ }
+ });
+ sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@Override
@SuppressWarnings("unchecked")
@@ -1161,6 +1173,26 @@
return ret;
}
+ private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
+ int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ ArrayList<Integer> caps = new ArrayList<Integer>();
+ caps.ensureCapacity(capabilities.length);
+ for (int c : capabilities) {
+ caps.add(new Integer(c));
+ }
+ int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
+ mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap());
+ List<MandatoryStreamCombination> combs = build.getAvailableMandatoryStreamCombinations();
+ if ((combs != null) && (!combs.isEmpty())) {
+ MandatoryStreamCombination[] combArray = new MandatoryStreamCombination[combs.size()];
+ combArray = combs.toArray(combArray);
+ return combArray;
+ }
+
+ return null;
+ }
+
private StreamConfigurationMap getStreamConfigurationMap() {
StreamConfiguration[] configurations = getBase(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -1433,6 +1465,31 @@
return true;
}
+ private int mCameraId = -1;
+ private Size mDisplaySize = new Size(0, 0);
+
+ /**
+ * Set the current camera Id.
+ *
+ * @param cameraId Current camera id.
+ *
+ * @hide
+ */
+ public void setCameraId(int cameraId) {
+ mCameraId = cameraId;
+ }
+
+ /**
+ * Set the current display size.
+ *
+ * @param displaySize The current display size.
+ *
+ * @hide
+ */
+ public void setDisplaySize(Size displaySize) {
+ mDisplaySize = displaySize;
+ }
+
@UnsupportedAppUsage
private long mMetadataPtr; // native CameraMetadata*
@@ -1476,6 +1533,8 @@
*/
public void swap(CameraMetadataNative other) {
nativeSwap(other);
+ mCameraId = other.mCameraId;
+ mDisplaySize = other.mDisplaySize;
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 123eb8e..3e130c5 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -39,6 +39,7 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
+import android.util.Size;
import android.util.SparseArray;
import android.view.Surface;
@@ -356,7 +357,7 @@
}
public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
- int cameraId) {
+ int cameraId, Size displaySize) {
if (DEBUG) {
Log.d(TAG, "Opening shim Camera device");
}
@@ -393,7 +394,8 @@
}
CameraCharacteristics characteristics =
- LegacyMetadataMapper.createCharacteristics(legacyParameters, info);
+ LegacyMetadataMapper.createCharacteristics(legacyParameters, info, cameraId,
+ displaySize);
LegacyCameraDevice device = new LegacyCameraDevice(
cameraId, legacyCamera, characteristics, threadCallbacks);
return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks);
@@ -482,8 +484,38 @@
@Override
public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig) {
- // TODO: Add support for this in legacy mode
- throw new UnsupportedOperationException("Session configuration query not supported!");
+ if (sessionConfig.getSessionType() != SessionConfiguration.SESSION_REGULAR) {
+ Log.e(TAG, "Session type: " + sessionConfig.getSessionType() + " is different from " +
+ " regular. Legacy devices support only regular session types!");
+ return false;
+ }
+
+ if (sessionConfig.getInputConfiguration() != null) {
+ Log.e(TAG, "Input configuration present, legacy devices do not support this feature!");
+ return false;
+ }
+
+ List<OutputConfiguration> outputConfigs = sessionConfig.getOutputConfigurations();
+ if (outputConfigs.isEmpty()) {
+ Log.e(TAG, "Empty output configuration list!");
+ return false;
+ }
+
+ SparseArray<Surface> surfaces = new SparseArray<Surface>(outputConfigs.size());
+ int idx = 0;
+ for (OutputConfiguration outputConfig : outputConfigs) {
+ List<Surface> surfaceList = outputConfig.getSurfaces();
+ if (surfaceList.isEmpty() || (surfaceList.size() > 1)) {
+ Log.e(TAG, "Legacy devices do not support deferred or shared surfaces!");
+ return false;
+ }
+
+ surfaces.put(idx++, outputConfig.getSurface());
+ }
+
+ int ret = mLegacyDevice.configureOutputs(surfaces, /*validateSurfacesOnly*/true);
+
+ return ret == LegacyExceptionUtils.NO_ERROR;
}
@Override
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 71a361b..aff09f2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -346,6 +346,25 @@
* on success.
*/
public int configureOutputs(SparseArray<Surface> outputs) {
+ return configureOutputs(outputs, /*validateSurfacesOnly*/false);
+ }
+
+ /**
+ * Configure the device with a set of output surfaces.
+ *
+ * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
+ *
+ * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
+ *
+ * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
+ * list; it must not be modified by the caller once it's passed in.
+ * @param validateSurfacesOnly If set it will only check whether the outputs are supported
+ * and avoid any device configuration.
+ * @return an error code for this binder operation, or {@link NO_ERROR}
+ * on success.
+ * @hide
+ */
+ public int configureOutputs(SparseArray<Surface> outputs, boolean validateSurfacesOnly) {
List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
if (outputs != null) {
int count = outputs.size();
@@ -397,7 +416,9 @@
sizedSurfaces.add(new Pair<>(output, s));
}
// Lock down the size before configuration
- setSurfaceDimens(output, s.getWidth(), s.getHeight());
+ if (!validateSurfacesOnly) {
+ setSurfaceDimens(output, s.getWidth(), s.getHeight());
+ }
} catch (BufferQueueAbandonedException e) {
Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
return BAD_VALUE;
@@ -406,6 +427,10 @@
}
}
+ if (validateSurfacesOnly) {
+ return LegacyExceptionUtils.NO_ERROR;
+ }
+
boolean success = false;
if (mDeviceState.setConfiguring()) {
mRequestThreadManager.configure(sizedSurfaces);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 8822f71..6953a5b 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -111,13 +111,15 @@
*
* @param parameters A non-{@code null} parameters set
* @param info Camera info with camera facing direction and angle of orientation
+ * @param cameraId Current camera Id
+ * @param displaySize Device display size
*
* @return static camera characteristics for a camera device
*
* @throws NullPointerException if any of the args were {@code null}
*/
public static CameraCharacteristics createCharacteristics(Camera.Parameters parameters,
- CameraInfo info) {
+ CameraInfo info, int cameraId, Size displaySize) {
checkNotNull(parameters, "parameters must not be null");
checkNotNull(info, "info must not be null");
@@ -125,7 +127,7 @@
android.hardware.CameraInfo outerInfo = new android.hardware.CameraInfo();
outerInfo.info = info;
- return createCharacteristics(paramStr, outerInfo);
+ return createCharacteristics(paramStr, outerInfo, cameraId, displaySize);
}
/**
@@ -134,12 +136,14 @@
*
* @param parameters A string parseable by {@link Camera.Parameters#unflatten}
* @param info Camera info with camera facing direction and angle of orientation
+ * @param cameraId Current camera id
+ * @param displaySize Device display size
* @return static camera characteristics for a camera device
*
* @throws NullPointerException if any of the args were {@code null}
*/
public static CameraCharacteristics createCharacteristics(String parameters,
- android.hardware.CameraInfo info) {
+ android.hardware.CameraInfo info, int cameraId, Size displaySize) {
checkNotNull(parameters, "parameters must not be null");
checkNotNull(info, "info must not be null");
checkNotNull(info.info, "info.info must not be null");
@@ -159,6 +163,9 @@
Log.v(TAG, "--------------------------------------------------- (end)");
}
+ m.setCameraId(cameraId);
+ m.setDisplaySize(displaySize);
+
return new CameraCharacteristics(m);
}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
new file mode 100644
index 0000000..b8e2d7a
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -0,0 +1,1219 @@
+/*
+ * 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.hardware.camera2.params;
+
+import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCharacteristics.Key;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.HashCodeHelpers;
+import android.graphics.PixelFormat;
+import android.media.CamcorderProfile;
+import android.util.Size;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Immutable class to store the available mandatory stream combination.
+ *
+ * <p>The individual stream combinations are generated according to the guidelines
+ * at {@link CameraDevice#createCaptureSession}.</p>
+ *
+ * <p>The list of stream combinations is available by invoking
+ * {@link CameraCharacteristics#get} and passing key
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS}.</p>
+ */
+public final class MandatoryStreamCombination {
+ private static final String TAG = "MandatoryStreamCombination";
+ /**
+ * Immutable class to store available mandatory stream information.
+ */
+ public static final class MandatoryStreamInformation {
+ private final int mFormat;
+ private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> ();
+ private final boolean mIsInput;
+
+ /**
+ * Create a new {@link MandatoryStreamInformation}.
+ *
+ @param availableSizes List of possible stream sizes.
+ * @param format Image format.
+ *
+ * @throws IllegalArgumentException
+ * if sizes is empty or if the format was not user-defined in
+ * ImageFormat/PixelFormat.
+ * @hide
+ */
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, int format) {
+ this(availableSizes, format, /*isInput*/false);
+ }
+
+ /**
+ * Create a new {@link MandatoryStreamInformation}.
+ *
+ @param availableSizes List of possible stream sizes.
+ * @param format Image format.
+ * @param isInput Flag indicating whether this stream is input.
+ *
+ * @throws IllegalArgumentException
+ * if sizes is empty or if the format was not user-defined in
+ * ImageFormat/PixelFormat.
+ * @hide
+ */
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, int format,
+ boolean isInput) {
+ if (availableSizes.isEmpty()) {
+ throw new IllegalArgumentException("No available sizes");
+ }
+ mAvailableSizes.addAll(availableSizes);
+ mFormat = checkArgumentFormat(format);
+ mIsInput = isInput;
+ }
+
+ /**
+ * Confirms whether or not this is an input stream.
+ * @return true in case the stream is input, false otherwise.
+ */
+ public boolean isInput() {
+ return mIsInput;
+ }
+
+ /**
+ * Return the list of available sizes for this mandatory stream.
+ *
+ * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
+ * resolution in the result will be tested and guaranteed to work. If clients want to use
+ * smaller sizes, then the resulting
+ * {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
+ * be tested either by calling {@link CameraDevice#createCaptureSession} or
+ * {@link CameraDevice#isSessionConfigurationSupported}.
+ *
+ * @return non-modifiable ascending list of available sizes.
+ */
+ public List<Size> getAvailableSizes() {
+ return Collections.unmodifiableList(mAvailableSizes);
+ }
+
+ /**
+ * Retrieve the mandatory stream {@code format}.
+ *
+ * @return integer format.
+ */
+ public int getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Check if this {@link MandatoryStreamInformation} is equal to another
+ * {@link MandatoryStreamInformation}.
+ *
+ * <p>Two vectors are only equal if and only if each of the respective elements is
+ * equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MandatoryStreamInformation) {
+ final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
+ if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
+ (mAvailableSizes.size() != other.mAvailableSizes.size())) {
+ return false;
+ }
+
+ return mAvailableSizes.equals(other.mAvailableSizes);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
+ mAvailableSizes.hashCode());
+ }
+ }
+
+ private final String mDescription;
+ private final boolean mIsReprocessable;
+ private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
+ new ArrayList<MandatoryStreamInformation>();
+ /**
+ * Create a new {@link MandatoryStreamCombination}.
+ *
+ * @param streamsInformation list of available streams in the stream combination.
+ * @param description Summary of the stream combination use case.
+ * @param isReprocessable Flag whether the mandatory stream combination is reprocessable.
+ *
+ * @throws IllegalArgumentException
+ * if stream information is empty
+ * @hide
+ */
+ public MandatoryStreamCombination(@NonNull List<MandatoryStreamInformation> streamsInformation,
+ String description, boolean isReprocessable) {
+ if (streamsInformation.isEmpty()) {
+ throw new IllegalArgumentException("Empty stream information");
+ }
+ mStreamsInformation.addAll(streamsInformation);
+ mDescription = description;
+ mIsReprocessable = isReprocessable;
+ }
+
+ /**
+ * Get the mandatory stream combination description.
+ *
+ * @return String with the mandatory combination description.
+ */
+ public String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Indicates whether the mandatory stream combination is reprocessable.
+ *
+ * @return {@code true} in case the mandatory stream combination contains an input,
+ * {@code false} otherwise.
+ */
+ public boolean isReprocessable() {
+ return mIsReprocessable;
+ }
+
+ /**
+ * Get information about each stream in the mandatory combination.
+ *
+ * @return Non-modifiable list of stream information.
+ *
+ */
+ public List<MandatoryStreamInformation> getStreamsInformation() {
+ return Collections.unmodifiableList(mStreamsInformation);
+ }
+
+ /**
+ * Check if this {@link MandatoryStreamCombination} is equal to another
+ * {@link MandatoryStreamCombination}.
+ *
+ * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MandatoryStreamCombination) {
+ final MandatoryStreamCombination other = (MandatoryStreamCombination) obj;
+ if ((mDescription != other.mDescription) ||
+ (mIsReprocessable != other.mIsReprocessable) ||
+ (mStreamsInformation.size() != other.mStreamsInformation.size())) {
+ return false;
+ }
+
+ return mStreamsInformation.equals(other.mStreamsInformation);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(Boolean.hashCode(mIsReprocessable), mDescription.hashCode(),
+ mStreamsInformation.hashCode());
+ }
+
+ private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM }
+ private static enum ReprocessType { NONE, PRIVATE, YUV }
+ private static final class StreamTemplate {
+ public int mFormat;
+ public SizeThreshold mSizeThreshold;
+ public boolean mIsInput;
+ public StreamTemplate(int format, SizeThreshold sizeThreshold) {
+ this(format, sizeThreshold, /*isInput*/false);
+ }
+
+ public StreamTemplate(int format, SizeThreshold sizeThreshold, boolean isInput) {
+ mFormat = format;
+ mSizeThreshold = sizeThreshold;
+ mIsInput = isInput;
+ }
+ }
+
+ private static final class StreamCombinationTemplate {
+ public StreamTemplate[] mStreamTemplates;
+ public String mDescription;
+ public ReprocessType mReprocessType;
+
+ public StreamCombinationTemplate(StreamTemplate[] streamTemplates, String description) {
+ this(streamTemplates, description, /*reprocessType*/ReprocessType.NONE);
+ }
+
+ public StreamCombinationTemplate(StreamTemplate[] streamTemplates, String description,
+ ReprocessType reprocessType) {
+ mStreamTemplates = streamTemplates;
+ mReprocessType = reprocessType;
+ mDescription = description;
+ }
+ }
+
+ private static StreamCombinationTemplate sLegacyCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
+ "Simple preview, GPU video processing, or no-preview video recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "No-viewfinder still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "In-application video/image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "Standard still imaging"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "In-app processing plus still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
+ "Standard recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
+ "Preview plus in-app processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "Still capture plus in-app processing")
+ };
+
+ private static StreamCombinationTemplate sLimitedCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
+ "High-resolution video recording with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
+ "High-resolution in-app video processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
+ "Two-input in-app video processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
+ "High-resolution recording with video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD) },
+ "High-resolution in-app processing with video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "Two-input in-app processing with still capture")
+ };
+
+ private static StreamCombinationTemplate sBurstCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution GPU processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution in-app processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution two-input in-app processsing")
+ };
+
+ private static StreamCombinationTemplate sFullCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution GPU processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution in-app processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution two-input in-app processsing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "Video recording with maximum-size video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Standard video recording plus maximum-resolution in-app processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Preview plus two-input maximum-resolution in-app processing")
+ };
+
+ private static StreamCombinationTemplate sRawCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "No-preview DNG capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Standard DNG capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "In-app processing plus DNG capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Video recording with DNG capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Preview with in-app processing and DNG capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Two-input in-app processing plus DNG capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Still capture with simultaneous JPEG and DNG"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "In-app processing with simultaneous JPEG and DNG")
+ };
+
+ private static StreamCombinationTemplate sLevel3Combinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "In-app viewfinder analysis with dynamic selection of output format"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "In-app viewfinder analysis with dynamic selection of output format")
+ };
+
+ private static StreamCombinationTemplate sLimitedPrivateReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "No-viewfinder still image reprocessing",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL(Zero-Shutter-Lag) still imaging",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL still and in-app processing imaging",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL in-app processing with still capture",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ };
+
+ private static StreamCombinationTemplate sLimitedYUVReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "No-viewfinder still image reprocessing",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL(Zero-Shutter-Lag) still imaging",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL still and in-app processing imaging",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL in-app processing with still capture",
+ /*reprocessType*/ ReprocessType.YUV),
+ };
+
+ private static StreamCombinationTemplate sFullPrivateReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
+ "High-resolution ZSL in-app video processing with regular preview",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution ZSL in-app processing with regular preview",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
+ "Maximum-resolution two-input ZSL in-app processing",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL still capture and in-app processing",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ };
+
+ private static StreamCombinationTemplate sFullYUVReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW) },
+ "Maximum-resolution multi-frame image fusion in-app processing with regular "
+ + "preview",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW) },
+ "Maximum-resolution multi-frame image fusion two-input in-app processing",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD) },
+ "High-resolution ZSL in-app video processing with regular preview",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "ZSL still capture and in-app processing",
+ /*reprocessType*/ ReprocessType.YUV),
+ };
+
+ private static StreamCombinationTemplate sRAWPrivateReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL in-app processing and DNG capture",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL in-app processing and preview with DNG capture",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL two-input in-app processing and DNG capture",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL still capture and preview with DNG capture",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ };
+
+ private static StreamCombinationTemplate sRAWYUVReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL in-app processing and DNG capture",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL in-app processing and preview with DNG capture",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL two-input in-app processing and DNG capture",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL still capture and preview with DNG capture",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "Mutually exclusive ZSL in-app processing with still capture and DNG capture",
+ /*reprocessType*/ ReprocessType.YUV),
+ };
+
+ private static StreamCombinationTemplate sLevel3PrivateReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
+ /*reprocessType*/ ReprocessType.PRIVATE),
+ };
+
+ private static StreamCombinationTemplate sLevel3YUVReprocCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM) },
+ "In-app viewfinder analysis with ZSL and RAW",
+ /*reprocessType*/ ReprocessType.YUV),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.VGA),
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM) },
+ "In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output",
+ /*reprocessType*/ ReprocessType.YUV),
+ };
+
+ /**
+ * Helper builder class to generate a list of available mandatory stream combinations.
+ * @hide
+ */
+ public static final class Builder {
+ private Size mDisplaySize;
+ private List<Integer> mCapabilities;
+ private int mHwLevel, mCameraId;
+ private StreamConfigurationMap mStreamConfigMap;
+
+ private final Size kPreviewSizeBound = new Size(1920, 1088);
+
+ /**
+ * Helper class to be used to generate the available mandatory stream combinations.
+ *
+ * @param cameraId Current camera id.
+ * @param hwLevel The camera HW level as reported by android.info.supportedHardwareLevel.
+ * @param displaySize The device display size.
+ * @param capabilities The camera device capabilities.
+ * @param sm The camera device stream configuration map.
+ */
+ public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
+ @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm) {
+ mCameraId = cameraId;
+ mDisplaySize = displaySize;
+ mCapabilities = capabilities;
+ mStreamConfigMap = sm;
+ mHwLevel = hwLevel;
+ }
+
+ /**
+ * Retrieve a list of all available mandatory stream combinations.
+ *
+ * @return a non-modifiable list of supported mandatory stream combinations or
+ * null in case device is not backward compatible or the method encounters
+ * an error.
+ */
+ public List<MandatoryStreamCombination> getAvailableMandatoryStreamCombinations() {
+ if (!isColorOutputSupported()) {
+ Log.v(TAG, "Device is not backward compatible!");
+ return null;
+ }
+
+ if ((mCameraId < 0) && !isExternalCamera()) {
+ Log.i(TAG, "Invalid camera id");
+ return null;
+ }
+
+ ArrayList<StreamCombinationTemplate> availableTemplates =
+ new ArrayList<StreamCombinationTemplate> ();
+ if (isHardwareLevelAtLeastLegacy()) {
+ availableTemplates.addAll(Arrays.asList(sLegacyCombinations));
+ }
+
+ // External devices are identical to limited devices w.r.t. stream combinations.
+ if (isHardwareLevelAtLeastLimited() || isExternalCamera()) {
+ availableTemplates.addAll(Arrays.asList(sLimitedCombinations));
+
+ if (isPrivateReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sLimitedPrivateReprocCombinations));
+ }
+
+ if (isYUVReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sLimitedYUVReprocCombinations));
+ }
+
+ }
+
+ if (isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
+ availableTemplates.addAll(Arrays.asList(sBurstCombinations));
+ }
+
+ if (isHardwareLevelAtLeastFull()) {
+ availableTemplates.addAll(Arrays.asList(sFullCombinations));
+
+ if (isPrivateReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sFullPrivateReprocCombinations));
+ }
+
+ if (isYUVReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sFullYUVReprocCombinations));
+ }
+
+ }
+
+ if (isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ availableTemplates.addAll(Arrays.asList(sRawCombinations));
+
+ if (isPrivateReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sRAWPrivateReprocCombinations));
+ }
+
+ if (isYUVReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sRAWYUVReprocCombinations));
+ }
+
+ }
+
+ if (isHardwareLevelAtLeastLevel3()) {
+ availableTemplates.addAll(Arrays.asList(sLevel3Combinations));
+
+ if (isPrivateReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sLevel3PrivateReprocCombinations));
+ }
+
+ if (isYUVReprocessingSupported()) {
+ availableTemplates.addAll(Arrays.asList(sLevel3YUVReprocCombinations));
+ }
+
+ }
+
+ return generateAvailableCombinations(availableTemplates);
+ }
+
+ /**
+ * Helper method to generate the available stream combinations given the
+ * list of available combination templates.
+ *
+ * @param availableTemplates a list of templates supported by the camera device.
+ * @return a non-modifiable list of supported mandatory stream combinations or
+ * null in case of errors.
+ */
+ private List<MandatoryStreamCombination> generateAvailableCombinations(
+ ArrayList<StreamCombinationTemplate> availableTemplates) {
+ if (availableTemplates.isEmpty()) {
+ Log.e(TAG, "No available stream templates!");
+ return null;
+ }
+
+ HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
+ enumerateAvailableSizes();
+ if (availableSizes == null) {
+ Log.e(TAG, "Available size enumeration failed!");
+ return null;
+ }
+
+ // RAW only uses MAXIMUM size threshold
+ Size[] rawSizes = mStreamConfigMap.getOutputSizes(ImageFormat.RAW_SENSOR);
+ ArrayList<Size> availableRawSizes = new ArrayList<Size>();
+ if (rawSizes != null) {
+ availableRawSizes.ensureCapacity(rawSizes.length);
+ availableRawSizes.addAll(Arrays.asList(rawSizes));
+ }
+
+ Size maxPrivateInputSize = new Size(0, 0);
+ if (isPrivateReprocessingSupported()) {
+ maxPrivateInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
+ ImageFormat.PRIVATE));
+ }
+
+ Size maxYUVInputSize = new Size(0, 0);
+ if (isYUVReprocessingSupported()) {
+ maxYUVInputSize = getMaxSize(mStreamConfigMap.getInputSizes(
+ ImageFormat.YUV_420_888));
+ }
+
+ // Generate the available mandatory stream combinations given the supported templates
+ // and size ranges.
+ ArrayList<MandatoryStreamCombination> availableStreamCombinations =
+ new ArrayList<MandatoryStreamCombination>();
+ availableStreamCombinations.ensureCapacity(availableTemplates.size());
+ for (StreamCombinationTemplate combTemplate : availableTemplates) {
+ ArrayList<MandatoryStreamInformation> streamsInfo =
+ new ArrayList<MandatoryStreamInformation>();
+ streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+ boolean isReprocessable = combTemplate.mReprocessType != ReprocessType.NONE;
+ if (isReprocessable) {
+ // The first and second streams in a reprocessable combination have the
+ // same size and format. The first is the input and the second is the output
+ // used for generating the subsequent input buffers.
+ ArrayList<Size> inputSize = new ArrayList<Size>();
+ int format;
+ if (combTemplate.mReprocessType == ReprocessType.PRIVATE) {
+ inputSize.add(maxPrivateInputSize);
+ format = ImageFormat.PRIVATE;
+ } else {
+ inputSize.add(maxYUVInputSize);
+ format = ImageFormat.YUV_420_888;
+ }
+
+ streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
+ /*isInput*/true));
+ streamsInfo.add(new MandatoryStreamInformation(inputSize, format));
+ }
+
+ for (StreamTemplate template : combTemplate.mStreamTemplates) {
+ List<Size> sizes = null;
+ if (template.mFormat == ImageFormat.RAW_SENSOR) {
+ sizes = availableRawSizes;
+ } else {
+ Pair<SizeThreshold, Integer> pair;
+ pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
+ new Integer(template.mFormat));
+ sizes = availableSizes.get(pair);
+ }
+
+ MandatoryStreamInformation streamInfo;
+ try {
+ streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No available sizes found for format: " + template.mFormat +
+ " size threshold: " + template.mSizeThreshold + " combination: " +
+ combTemplate.mDescription);
+ return null;
+ }
+
+ streamsInfo.add(streamInfo);
+ }
+
+ MandatoryStreamCombination streamCombination;
+ try {
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ combTemplate.mDescription, isReprocessable);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No stream information for mandatory combination: "
+ + combTemplate.mDescription);
+ return null;
+ }
+
+ availableStreamCombinations.add(streamCombination);
+ }
+
+ return Collections.unmodifiableList(availableStreamCombinations);
+ }
+
+ /**
+ * Helper method to enumerate all available sizes according to size threshold and format.
+ */
+ private HashMap<Pair<SizeThreshold, Integer>, List<Size>> enumerateAvailableSizes() {
+ final int[] formats = {
+ ImageFormat.PRIVATE,
+ ImageFormat.YUV_420_888,
+ ImageFormat.JPEG
+ };
+ Size recordingMaxSize = new Size(0, 0);
+ Size previewMaxSize = new Size(0, 0);
+ Size vgaSize = new Size(640, 480);
+ if (isExternalCamera()) {
+ recordingMaxSize = getMaxExternalRecordingSize();
+ } else {
+ recordingMaxSize = getMaxRecordingSize();
+ }
+ if (recordingMaxSize == null) {
+ Log.e(TAG, "Failed to find maximum recording size!");
+ return null;
+ }
+
+ HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
+ for (int format : formats) {
+ Integer intFormat = new Integer(format);
+ allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
+ }
+
+ List<Size> previewSizes = getSizesWithinBound(
+ allSizes.get(new Integer(ImageFormat.PRIVATE)), kPreviewSizeBound);
+ if ((previewSizes == null) || (previewSizes.isEmpty())) {
+ Log.e(TAG, "No preview sizes within preview size bound!");
+ return null;
+ }
+ List<Size> orderedPreviewSizes = getAscendingOrderSizes(previewSizes,
+ /*ascending*/false);
+ previewMaxSize = getMaxPreviewSize(orderedPreviewSizes);
+
+ HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
+ new HashMap<Pair<SizeThreshold, Integer>, List<Size>>();
+
+ for (int format : formats) {
+ Integer intFormat = new Integer(format);
+ Size[] sizes = allSizes.get(intFormat);
+ Pair<SizeThreshold, Integer> pair = new Pair<SizeThreshold, Integer>(
+ SizeThreshold.VGA, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, vgaSize));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.PREVIEW, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, previewMaxSize));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.RECORD, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, recordingMaxSize));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
+ availableSizes.put(pair, Arrays.asList(sizes));
+ }
+
+ return availableSizes;
+ }
+
+ /**
+ * Compile a list of sizes smaller than or equal to given bound.
+ * Return an empty list if there is no size smaller than or equal to the bound.
+ */
+ private static List<Size> getSizesWithinBound(Size[] sizes, Size bound) {
+ if (sizes == null || sizes.length == 0) {
+ Log.e(TAG, "Empty or invalid size array!");
+ return null;
+ }
+
+ ArrayList<Size> ret = new ArrayList<Size>();
+ for (Size size : sizes) {
+ if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
+ ret.add(size);
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Get the largest size by area.
+ *
+ * @param sizes an array of sizes, must have at least 1 element
+ *
+ * @return Largest Size
+ *
+ * @throws IllegalArgumentException if sizes was null or had 0 elements
+ */
+ public static Size getMaxSize(Size... sizes) {
+ if (sizes == null || sizes.length == 0) {
+ throw new IllegalArgumentException("sizes was empty");
+ }
+
+ Size sz = sizes[0];
+ for (Size size : sizes) {
+ if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
+ sz = size;
+ }
+ }
+
+ return sz;
+ }
+
+ /**
+ * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
+ * at least the desired one (but could be higher)
+ */
+ private boolean isHardwareLevelAtLeast(int level) {
+ final int[] sortedHwLevels = {
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ };
+ if (level == mHwLevel) {
+ return true;
+ }
+
+ for (int sortedlevel : sortedHwLevels) {
+ if (sortedlevel == level) {
+ return true;
+ } else if (sortedlevel == mHwLevel) {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Whether or not the camera is an external camera.
+ *
+ * @return {@code true} if the device is external, {@code false} otherwise.
+ */
+ private boolean isExternalCamera() {
+ return mHwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+ }
+
+ /**
+ * Whether or not the hardware level is at least legacy.
+ *
+ * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
+ */
+ private boolean isHardwareLevelAtLeastLegacy() {
+ return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
+ }
+
+ /**
+ * Whether or not the hardware level is at least limited.
+ *
+ * @return {@code true} if the device is {@code LIMITED} or {@code FULL},
+ * {@code false} otherwise (i.e. LEGACY).
+ */
+ private boolean isHardwareLevelAtLeastLimited() {
+ return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
+ }
+
+ /**
+ * Whether or not the hardware level is at least full.
+ *
+ * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
+ */
+ private boolean isHardwareLevelAtLeastFull() {
+ return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
+ }
+
+ /**
+ * Whether or not the hardware level is at least Level 3.
+ *
+ * @return {@code true} if the device is {@code LEVEL3}, {@code false} otherwise.
+ */
+ private boolean isHardwareLevelAtLeastLevel3() {
+ return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3);
+ }
+
+ /**
+ * Determine whether the current device supports a capability or not.
+ *
+ * @param capability (non-negative)
+ *
+ * @return {@code true} if the capability is supported, {@code false} otherwise.
+ *
+ */
+ private boolean isCapabilitySupported(int capability) {
+ return mCapabilities.contains(capability);
+ }
+
+ /**
+ * Check whether the current device is backward compatible.
+ */
+ private boolean isColorOutputSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+ }
+
+ /**
+ * Check whether the current device supports private reprocessing.
+ */
+ private boolean isPrivateReprocessingSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
+ }
+
+ /**
+ * Check whether the current device supports YUV reprocessing.
+ */
+ private boolean isYUVReprocessingSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
+ }
+
+ /**
+ * Return the maximum supported video size using the camcorder profile information.
+ *
+ * @return Maximum supported video size.
+ */
+ private Size getMaxRecordingSize() {
+ int quality =
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_2160P) ?
+ CamcorderProfile.QUALITY_2160P :
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P) ?
+ CamcorderProfile.QUALITY_1080P :
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P) ?
+ CamcorderProfile.QUALITY_720P :
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P) ?
+ CamcorderProfile.QUALITY_480P :
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA) ?
+ CamcorderProfile.QUALITY_QVGA :
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_CIF) ?
+ CamcorderProfile.QUALITY_CIF :
+ CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QCIF) ?
+ CamcorderProfile.QUALITY_QCIF :
+ -1;
+
+ if (quality < 0) {
+ return null;
+ }
+
+ CamcorderProfile maxProfile = CamcorderProfile.get(mCameraId, quality);
+ return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
+ }
+
+ /**
+ * Return the maximum supported video size for external cameras using data from
+ * the stream configuration map.
+ *
+ * @return Maximum supported video size.
+ */
+ private Size getMaxExternalRecordingSize() {
+ final Size FULLHD = new Size(1920, 1080);
+
+ Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
+ android.media.MediaRecorder.class);
+ List<Size> sizes = new ArrayList<Size>();
+ for (Size sz: videoSizeArr) {
+ if (sz.getWidth() <= FULLHD.getWidth() && sz.getHeight() <= FULLHD.getHeight()) {
+ sizes.add(sz);
+ }
+ }
+ List<Size> videoSizes = getAscendingOrderSizes(sizes, /*ascending*/false);
+ for (Size sz : videoSizes) {
+ long minFrameDuration = mStreamConfigMap.getOutputMinFrameDuration(
+ android.media.MediaRecorder.class, sz);
+ // Give some margin for rounding error
+ if (minFrameDuration > (1e9 / 30.1)) {
+ Log.i(TAG, "External camera " + mCameraId + " has max video size:" + sz);
+ return sz;
+ }
+ }
+ Log.w(TAG, "Camera " + mCameraId + " does not support any 30fps video output");
+ return FULLHD; // doesn't matter what size is returned here
+ }
+
+ private Size getMaxPreviewSize(List<Size> orderedPreviewSizes) {
+ if (orderedPreviewSizes != null) {
+ for (Size size : orderedPreviewSizes) {
+ if ((mDisplaySize.getWidth() >= size.getWidth()) &&
+ (mDisplaySize.getWidth() >= size.getHeight())) {
+ return size;
+ }
+ }
+ }
+
+ Log.w(TAG,"Camera " + mCameraId + " maximum preview size search failed with "
+ + "display size " + mDisplaySize);
+ return kPreviewSizeBound;
+ }
+
+ /**
+ * Size comparison method used by size comparators.
+ */
+ private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
+ long left = widthA * (long) heightA;
+ long right = widthB * (long) heightB;
+ if (left == right) {
+ left = widthA;
+ right = widthB;
+ }
+ return (left < right) ? -1 : (left > right ? 1 : 0);
+ }
+
+ /**
+ * Size comparator that compares the number of pixels it covers.
+ *
+ * <p>If two the areas of two sizes are same, compare the widths.</p>
+ */
+ public static class SizeComparator implements Comparator<Size> {
+ @Override
+ public int compare(Size lhs, Size rhs) {
+ return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
+ rhs.getHeight());
+ }
+ }
+
+ /**
+ * Get a sorted list of sizes from a given size list.
+ *
+ * <p>
+ * The size is compare by area it covers, if the areas are same, then
+ * compare the widths.
+ * </p>
+ *
+ * @param sizeList The input size list to be sorted
+ * @param ascending True if the order is ascending, otherwise descending order
+ * @return The ordered list of sizes
+ */
+ private static List<Size> getAscendingOrderSizes(final List<Size> sizeList,
+ boolean ascending) {
+ if (sizeList == null) {
+ return null;
+ }
+
+ Comparator<Size> comparator = new SizeComparator();
+ List<Size> sortedSizes = new ArrayList<Size>();
+ sortedSizes.addAll(sizeList);
+ Collections.sort(sortedSizes, comparator);
+ if (!ascending) {
+ Collections.reverse(sortedSizes);
+ }
+
+ return sortedSizes;
+ }
+ }
+}
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/Binder.java b/core/java/android/os/Binder.java
index d45fa11..9939a3c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -399,6 +400,9 @@
* reasons, we only support one UID. This UID represents the original user responsible for the
* binder calls.
*
+ * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after setting the
+ * worksource.
+ *
* <p>A typical use case would be
* <pre>
* long token = Binder.setCallingWorkSourceUid(uid);
@@ -417,6 +421,7 @@
* @hide
**/
@CriticalNative
+ @SystemApi
public static final native long setCallingWorkSourceUid(int workSource);
/**
@@ -430,6 +435,7 @@
* @hide
*/
@CriticalNative
+ @SystemApi
public static final native int getCallingWorkSourceUid();
/**
@@ -438,10 +444,24 @@
* <p>The work source will be propagated for future outgoing binder transactions
* executed on this thread.
*
+ * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after clearing the
+ * worksource.
+ *
+ * <p>A typical use case would be
+ * <pre>
+ * long token = Binder.clearCallingWorkSource();
+ * try {
+ * // Call an API.
+ * } finally {
+ * Binder.restoreCallingWorkSource(token);
+ * }
+ * </pre>
+ *
* @return token to restore original work source.
* @hide
**/
@CriticalNative
+ @SystemApi
public static final native long clearCallingWorkSource();
/**
@@ -461,6 +481,7 @@
* @hide
**/
@CriticalNative
+ @SystemApi
public static final native void restoreCallingWorkSource(long token);
/**
@@ -601,6 +622,7 @@
* See {@link setProxyTransactListener}.
* @hide
*/
+ @SystemApi
public interface ProxyTransactListener {
/**
* Called before onTransact.
@@ -663,6 +685,7 @@
* <li>Never execute another binder transaction inside the listener.
* @hide
*/
+ @SystemApi
public static void setProxyTransactListener(@Nullable ProxyTransactListener listener) {
BinderProxy.setTransactListener(listener);
}
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/CallLog.java b/core/java/android/provider/CallLog.java
index 3d93afd..de54a8aa 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -28,12 +28,14 @@
import android.location.Country;
import android.location.CountryDetector;
import android.net.Uri;
+import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract.CommonDataKinds.Callable;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.DataUsageFeedback;
+import android.telecom.CallIdentification;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -603,6 +605,69 @@
public static final String BLOCK_REASON = "block_reason";
/**
+ * The package name of the {@link android.telecom.CallScreeningService} which provided
+ * {@link android.telecom.CallIdentification} for this call.
+ * <P>Type: TEXT</P>
+ */
+ public static final String CALL_ID_PACKAGE_NAME = "call_id_package_name";
+
+ /**
+ * The app name of the {@link android.telecom.CallScreeningService} which provided
+ * {@link android.telecom.CallIdentification} for this call.
+ * <P>Type: TEXT</P>
+ */
+ public static final String CALL_ID_APP_NAME = "call_id_app_name";
+
+ /**
+ * The {@link CallIdentification#getName() name} of a call, as provided by the
+ * {@link android.telecom.CallScreeningService}.
+ * <p>
+ * The name is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
+ * {@link #CALL_ID_APP_NAME}.
+ * <P>Type: TEXT</P>
+ */
+ public static final String CALL_ID_NAME = "call_id_name";
+
+ /**
+ * The {@link CallIdentification#getDescription() description} of a call, as provided by the
+ * {@link android.telecom.CallScreeningService}.
+ * <p>
+ * The description is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
+ * {@link #CALL_ID_APP_NAME}.
+ * <P>Type: TEXT</P>
+ */
+ public static final String CALL_ID_DESCRIPTION = "call_id_description";
+
+ /**
+ * The {@link CallIdentification#getDetails() details} of a call, as provided by the
+ * {@link android.telecom.CallScreeningService}.
+ * <p>
+ * The details field is provided by the app identified by {@link #CALL_ID_PACKAGE_NAME} and
+ * {@link #CALL_ID_APP_NAME}.
+ * <P>Type: TEXT</P>
+ */
+ public static final String CALL_ID_DETAILS = "call_id_details";
+
+ /**
+ * The {@link CallIdentification#getNuisanceConfidence() nuisance confidence} of a call, as
+ * provided by the {@link android.telecom.CallScreeningService}.
+ * <p>
+ * Valid values are defined in {@link CallIdentification}, and include:
+ * <ul>
+ * <li>{@link CallIdentification#CONFIDENCE_NOT_NUISANCE}</li>
+ * <li>{@link CallIdentification#CONFIDENCE_LIKELY_NOT_NUISANCE}</li>
+ * <li>{@link CallIdentification#CONFIDENCE_UNKNOWN}</li>
+ * <li>{@link CallIdentification#CONFIDENCE_LIKELY_NUISANCE}</li>
+ * <li>{@link CallIdentification#CONFIDENCE_NUISANCE}</li>
+ * </ul>
+ * <p>
+ * The nuisance confidence is provided by the app identified by
+ * {@link #CALL_ID_PACKAGE_NAME} and {@link #CALL_ID_APP_NAME}.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String CALL_ID_NUISANCE_CONFIDENCE = "call_id_nuisance_confidence";
+
+ /**
* Adds a call to the call log.
*
* @param ci the CallerInfo object to get the target contact from. Can be null
@@ -631,7 +696,8 @@
presentation, callType, features, accountHandle, start, duration,
dataUsage, false /* addForAllUsers */, null /* userToBeInsertedTo */,
false /* isRead */, Calls.BLOCK_REASON_NOT_BLOCKED /* callBlockReason */,
- null /* callScreeningAppName */, null /* callScreeningComponentName */);
+ null /* callScreeningAppName */, null /* callScreeningComponentName */,
+ null /* callIdentification */);
}
@@ -671,7 +737,8 @@
features, accountHandle, start, duration, dataUsage, addForAllUsers,
userToBeInsertedTo, false /* isRead */ , Calls.BLOCK_REASON_NOT_BLOCKED
/* callBlockReason */, null /* callScreeningAppName */,
- null /* callScreeningComponentName */);
+ null /* callScreeningComponentName */,
+ null /* callIdentification */);
}
/**
@@ -705,19 +772,32 @@
* @param callBlockReason The reason why the call is blocked.
* @param callScreeningAppName The call screening application name which block the call.
* @param callScreeningComponentName The call screening component name which block the call.
+ * @param callIdPackageName The package name of the
+ * {@link android.telecom.CallScreeningService} which provided
+ * {@link CallIdentification}.
+ * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService}
+ * which provided {@link CallIdentification}.
+ * @param callIdName The caller name provided by the
+ * {@link android.telecom.CallScreeningService}.
+ * @param callIdDescription The caller description provided by the
+ * {@link android.telecom.CallScreeningService}.
+ * @param callIdDetails The caller details provided by the
+ * {@link android.telecom.CallScreeningService}.
+ * @param callIdCallType The caller type provided by the
+ * {@link android.telecom.CallScreeningService}.
*
* @result The URI of the call log entry belonging to the user that made or received this
* call. This could be of the shadow provider. Do not return it to non-system apps,
* as they don't have permissions.
* {@hide}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static Uri addCall(CallerInfo ci, Context context, String number,
String postDialDigits, String viaNumber, int presentation, int callType,
int features, PhoneAccountHandle accountHandle, long start, int duration,
Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
boolean isRead, int callBlockReason, String callScreeningAppName,
- String callScreeningComponentName) {
+ String callScreeningComponentName, CallIdentification callIdentification) {
if (VERBOSE_LOG) {
Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
number, userToBeInsertedTo, addForAllUsers));
@@ -799,6 +879,22 @@
values.put(CALL_SCREENING_APP_NAME, callScreeningAppName);
values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
+ if (callIdentification != null) {
+ values.put(CALL_ID_PACKAGE_NAME, callIdentification.getCallScreeningPackageName());
+ values.put(CALL_ID_APP_NAME, callIdentification.getCallScreeningAppName());
+ values.put(CALL_ID_NAME, callIdentification.getName());
+ values.put(CALL_ID_DESCRIPTION, callIdentification.getDescription());
+ values.put(CALL_ID_DETAILS, callIdentification.getDetails());
+ values.put(CALL_ID_NUISANCE_CONFIDENCE, callIdentification.getNuisanceConfidence());
+ } else {
+ values.putNull(CALL_ID_PACKAGE_NAME);
+ values.putNull(CALL_ID_APP_NAME);
+ values.putNull(CALL_ID_NAME);
+ values.putNull(CALL_ID_DESCRIPTION);
+ values.putNull(CALL_ID_DETAILS);
+ values.putNull(CALL_ID_NUISANCE_CONFIDENCE);
+ }
+
if ((ci != null) && (ci.contactIdOrZero > 0)) {
// Update usage information for the number associated with the contact ID.
// We need to use both the number and the ID for obtaining a data ID since other
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/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 9aff281..7683b8a 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -254,6 +254,15 @@
@GuardedBy("mLock")
private AutofillValue mFocusedValue;
+ /**
+ * Id of the last field that cause the Autofill UI to be shown.
+ *
+ * <p>Used to make sure the SmartSuggestionsParams is updated when a new fields is focused.
+ */
+ // TODO(b/111330312): might not be needed when using IME
+ @GuardedBy("mLock")
+ private AutofillId mLastShownId;
+
// Objects used to log metrics
private final long mRequestTime;
private long mOnSuccessTime;
@@ -284,7 +293,7 @@
@NonNull
public SystemPopupPresentationParams getSmartSuggestionParams() {
synchronized (mLock) {
- if (mSmartSuggestion != null) {
+ if (mSmartSuggestion != null && mFocusedId.equals(mLastShownId)) {
return mSmartSuggestion;
}
Rect rect;
@@ -299,6 +308,7 @@
return null;
}
mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+ mLastShownId = mFocusedId;
return mSmartSuggestion;
}
}
@@ -401,6 +411,9 @@
if (mFocusedValue != null) {
pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
}
+ if (mLastShownId != null) {
+ pw.print(prefix); pw.print("lastShownId: "); pw.println(mLastShownId);
+ }
pw.print(prefix); pw.print("client: "); pw.println(mClient);
final String prefix2 = prefix + " ";
if (mFillWindow != null) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 1fe97b7..6d2f856 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1482,7 +1482,7 @@
private boolean mShowBadge;
private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
private boolean mHidden;
- private boolean mAudiblyAlerted;
+ private long mLastAudiblyAlertedMs;
private boolean mNoisy;
private ArrayList<Notification.Action> mSmartActions;
private ArrayList<CharSequence> mSmartReplies;
@@ -1650,12 +1650,12 @@
}
/**
- * Returns whether this notification alerted the user via sound or vibration.
+ * Returns the last time this notification alerted the user via sound or vibration.
*
- * @return true if the notification alerted the user, false otherwise.
+ * @return the time of the last alerting behavior, in milliseconds.
*/
- public boolean audiblyAlerted() {
- return mAudiblyAlerted;
+ public long getLastAudiblyAlertedMillis() {
+ return mLastAudiblyAlertedMs;
}
/** @hide */
@@ -1672,7 +1672,7 @@
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
- int userSentiment, boolean hidden, boolean audiblyAlerted,
+ int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
ArrayList<CharSequence> smartReplies) {
mKey = key;
@@ -1690,7 +1690,7 @@
mShowBadge = showBadge;
mUserSentiment = userSentiment;
mHidden = hidden;
- mAudiblyAlerted = audiblyAlerted;
+ mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
mNoisy = noisy;
mSmartActions = smartActions;
mSmartReplies = smartReplies;
@@ -1743,7 +1743,7 @@
private ArrayMap<String, Boolean> mShowBadge;
private ArrayMap<String, Integer> mUserSentiment;
private ArrayMap<String, Boolean> mHidden;
- private ArrayMap<String, Boolean> mAudiblyAlerted;
+ private ArrayMap<String, Long> mLastAudiblyAlerted;
private ArrayMap<String, Boolean> mNoisy;
private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
@@ -1776,7 +1776,7 @@
getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
getShowBadge(key), getUserSentiment(key), getHidden(key),
- getAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
+ getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
getSmartReplies(key));
return rank >= 0;
}
@@ -1915,14 +1915,14 @@
return hidden == null ? false : hidden.booleanValue();
}
- private boolean getAudiblyAlerted(String key) {
+ private long getLastAudiblyAlerted(String key) {
synchronized (this) {
- if (mAudiblyAlerted == null) {
- buildAudiblyAlertedLocked();
+ if (mLastAudiblyAlerted == null) {
+ buildLastAudiblyAlertedLocked();
}
}
- Boolean audiblyAlerted = mAudiblyAlerted.get(key);
- return audiblyAlerted == null ? false : audiblyAlerted.booleanValue();
+ Long lastAudibleAlerted = mLastAudiblyAlerted.get(key);
+ return lastAudibleAlerted == null ? -1 : lastAudibleAlerted.longValue();
}
private boolean getNoisy(String key) {
@@ -1994,6 +1994,14 @@
return newMap;
}
+ private ArrayMap<String, Long> buildLongMapFromBundle(Bundle bundle) {
+ ArrayMap<String, Long> newMap = new ArrayMap<>(bundle.size());
+ for (String key : bundle.keySet()) {
+ newMap.put(key, bundle.getLong(key));
+ }
+ return newMap;
+ }
+
// Locked by 'this'
private void buildVisibilityOverridesLocked() {
mVisibilityOverrides = buildIntMapFromBundle(mRankingUpdate.getVisibilityOverrides());
@@ -2070,8 +2078,8 @@
}
// Locked by 'this'
- private void buildAudiblyAlertedLocked() {
- mAudiblyAlerted = buildBooleanMapFromBundle(mRankingUpdate.getAudiblyAlerted());
+ private void buildLastAudiblyAlertedLocked() {
+ mLastAudiblyAlerted = buildLongMapFromBundle(mRankingUpdate.getLastAudiblyAlerted());
}
// Locked by 'this'
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index f80df93..ebaeff8 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -39,7 +39,7 @@
private final Bundle mHidden;
private final Bundle mSmartActions;
private final Bundle mSmartReplies;
- private final Bundle mAudiblyAlerted;
+ private final Bundle mLastAudiblyAlerted;
private final Bundle mNoisy;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
@@ -47,7 +47,7 @@
int[] importance, Bundle explanation, Bundle overrideGroupKeys,
Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
- Bundle smartReplies, Bundle audiblyAlerted, Bundle noisy) {
+ Bundle smartReplies, Bundle lastAudiblyAlerted, Bundle noisy) {
mKeys = keys;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
@@ -63,7 +63,7 @@
mHidden = hidden;
mSmartActions = smartActions;
mSmartReplies = smartReplies;
- mAudiblyAlerted = audiblyAlerted;
+ mLastAudiblyAlerted = lastAudiblyAlerted;
mNoisy = noisy;
}
@@ -84,7 +84,7 @@
mHidden = in.readBundle();
mSmartActions = in.readBundle();
mSmartReplies = in.readBundle();
- mAudiblyAlerted = in.readBundle();
+ mLastAudiblyAlerted = in.readBundle();
mNoisy = in.readBundle();
}
@@ -110,7 +110,7 @@
out.writeBundle(mHidden);
out.writeBundle(mSmartActions);
out.writeBundle(mSmartReplies);
- out.writeBundle(mAudiblyAlerted);
+ out.writeBundle(mLastAudiblyAlerted);
out.writeBundle(mNoisy);
}
@@ -185,8 +185,8 @@
return mSmartReplies;
}
- public Bundle getAudiblyAlerted() {
- return mAudiblyAlerted;
+ public Bundle getLastAudiblyAlerted() {
+ return mLastAudiblyAlerted;
}
public Bundle getNoisy() {
diff --git a/core/java/android/service/textclassifier/ITextClassifierService.aidl b/core/java/android/service/textclassifier/ITextClassifierService.aidl
index 254a710..7941794 100644
--- a/core/java/android/service/textclassifier/ITextClassifierService.aidl
+++ b/core/java/android/service/textclassifier/ITextClassifierService.aidl
@@ -26,6 +26,7 @@
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextSelection;
@@ -52,10 +53,15 @@
in TextLinks.Request request,
in ITextLinksCallback callback);
+ // TODO: Remove
void onSelectionEvent(
in TextClassificationSessionId sessionId,
in SelectionEvent event);
+ void onTextClassifierEvent(
+ in TextClassificationSessionId sessionId,
+ in TextClassifierEvent event);
+
void onCreateTextClassificationSession(
in TextClassificationContext context,
in TextClassificationSessionId sessionId);
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 3b813c7..2221d6e 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -39,6 +39,7 @@
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -193,6 +194,15 @@
/** {@inheritDoc} */
@Override
+ public void onTextClassifierEvent(
+ TextClassificationSessionId sessionId,
+ TextClassifierEvent event) {
+ Preconditions.checkNotNull(event);
+ TextClassifierService.this.onTextClassifierEvent(sessionId, event);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public void onDetectLanguage(
TextClassificationSessionId sessionId,
TextLanguage.Request request,
@@ -368,11 +378,28 @@
*
* @param sessionId the session id
* @param event the selection event
+ * @deprecated
+ * Use {@link #onTextClassifierEvent(TextClassificationSessionId, TextClassifierEvent)}
+ * instead
*/
+ @Deprecated
public void onSelectionEvent(
@Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
/**
+ * Writes the TextClassifier event.
+ * This is called when a TextClassifier event occurs. e.g. user changed selection,
+ * smart selection happened, or a link was clicked.
+ *
+ * <p>The default implementation ignores the event.
+ *
+ * @param sessionId the session id
+ * @param event the TextClassifier event
+ */
+ public void onTextClassifierEvent(
+ @Nullable TextClassificationSessionId sessionId, @NonNull TextClassifierEvent event) {}
+
+ /**
* Creates a new text classification session for the specified context.
*
* @param context the text classification context
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/text/style/LineBackgroundSpan.java b/core/java/android/text/style/LineBackgroundSpan.java
index 5a55fd7..e43fd83 100644
--- a/core/java/android/text/style/LineBackgroundSpan.java
+++ b/core/java/android/text/style/LineBackgroundSpan.java
@@ -118,7 +118,7 @@
int lineNumber) {
final int originColor = paint.getColor();
paint.setColor(mColor);
- canvas.drawRect(left, right, top, bottom, paint);
+ canvas.drawRect(left, top, right, bottom, paint);
paint.setColor(originColor);
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c4be0e5..9dfd43c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -117,8 +117,10 @@
void stopFreezingScreen();
// these require DISABLE_KEYGUARD permission
- void disableKeyguard(IBinder token, String tag);
- void reenableKeyguard(IBinder token);
+ /** @deprecated use Activity.setShowWhenLocked instead. */
+ void disableKeyguard(IBinder token, String tag, int userId);
+ /** @deprecated use Activity.setShowWhenLocked instead. */
+ void reenableKeyguard(IBinder token, int userId);
void exitKeyguardSecurely(IOnKeyguardExitResult callback);
boolean isKeyguardLocked();
boolean isKeyguardSecure();
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/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 4a5ccdf..ada7853 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -340,7 +340,7 @@
}
/** Updates icon visibility based on the noisiness of the notification. */
- public void setAudiblyAlerted(boolean audiblyAlerted) {
+ public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9fe0ddc..9a1e931 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1876,6 +1876,7 @@
}
void dispatchApplyInsets(View host) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets");
WindowInsets insets = getWindowInsets(true /* forceConstruct */);
final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
== LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
@@ -1885,6 +1886,7 @@
insets = insets.consumeDisplayCutout();
}
host.dispatchApplyWindowInsets(insets);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
InsetsController getInsetsController() {
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/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 04924c9..3f690f7 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -139,17 +139,21 @@
*/
public static final String HINT_FOR_NOTIFICATION = "notification";
- private List<ConversationAction> mConversationActions;
+ private final List<ConversationAction> mConversationActions;
+ private final String mId;
/** Constructs a {@link ConversationActions} object. */
- public ConversationActions(@NonNull List<ConversationAction> conversationActions) {
+ public ConversationActions(
+ @NonNull List<ConversationAction> conversationActions, @Nullable String id) {
mConversationActions =
Collections.unmodifiableList(Preconditions.checkNotNull(conversationActions));
+ mId = id;
}
private ConversationActions(Parcel in) {
mConversationActions =
Collections.unmodifiableList(in.createTypedArrayList(ConversationAction.CREATOR));
+ mId = in.readString();
}
@Override
@@ -160,14 +164,26 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedList(mConversationActions);
+ parcel.writeString(mId);
}
- /** Returns an immutable list of {@link ConversationAction} objects. */
+ /**
+ * Returns an immutable list of {@link ConversationAction} objects, which are ordered from high
+ * confidence to low confidence.
+ */
@NonNull
public List<ConversationAction> getConversationActions() {
return mConversationActions;
}
+ /**
+ * Returns the id, if one exists, for this object.
+ */
+ @Nullable
+ public String getId() {
+ return mId;
+ }
+
/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
public static final class ConversationAction implements Parcelable {
@@ -678,35 +694,37 @@
private final List<String> mHints;
@Nullable
private String mCallingPackageName;
+ @Nullable
+ private final String mConversationId;
private Request(
@NonNull List<Message> conversation,
@NonNull TypeConfig typeConfig,
int maxSuggestions,
+ String conversationId,
@Nullable @Hint List<String> hints) {
mConversation = Preconditions.checkNotNull(conversation);
mTypeConfig = Preconditions.checkNotNull(typeConfig);
mMaxSuggestions = maxSuggestions;
+ mConversationId = conversationId;
mHints = hints;
}
private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
in.readParcelableList(conversation, null);
-
TypeConfig typeConfig = in.readParcelable(null);
-
int maxSuggestions = in.readInt();
-
+ String conversationId = in.readString();
List<String> hints = new ArrayList<>();
in.readStringList(hints);
-
String callingPackageName = in.readString();
Request request = new Request(
conversation,
typeConfig,
maxSuggestions,
+ conversationId,
hints);
request.setCallingPackageName(callingPackageName);
return request;
@@ -717,6 +735,7 @@
parcel.writeParcelableList(mConversation, flags);
parcel.writeParcelable(mTypeConfig, flags);
parcel.writeInt(mMaxSuggestions);
+ parcel.writeString(mConversationId);
parcel.writeStringList(mHints);
parcel.writeString(mCallingPackageName);
}
@@ -759,6 +778,16 @@
return mMaxSuggestions;
}
+ /**
+ * Return an unique identifier of the conversation that is generating actions for. This
+ * identifier is unique within the calling package only, so use it with
+ * {@link #getCallingPackageName()}.
+ */
+ @Nullable
+ public String getConversationId() {
+ return mConversationId;
+ }
+
/** Returns an immutable list of hints */
@Nullable
@Hint
@@ -794,6 +823,8 @@
private TypeConfig mTypeConfig;
private int mMaxSuggestions;
@Nullable
+ private String mConversationId;
+ @Nullable
@Hint
private List<String> mHints;
@@ -823,7 +854,8 @@
return this;
}
- /** Sets the maximum number of suggestions you want.
+ /**
+ * Sets the maximum number of suggestions you want.
* <p>
* Value 0 means no restriction.
*/
@@ -833,6 +865,15 @@
return this;
}
+ /**
+ * Sets an unique identifier of the conversation that is generating actions for.
+ */
+ @NonNull
+ public Builder setConversationId(@Nullable String conversationId) {
+ mConversationId = conversationId;
+ return this;
+ }
+
/** Builds the {@link Request} object. */
@NonNull
public Request build() {
@@ -840,6 +881,7 @@
Collections.unmodifiableList(mConversation),
mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
mMaxSuggestions,
+ mConversationId,
mHints == null
? Collections.emptyList()
: Collections.unmodifiableList(mHints));
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index c24489c..8b370f5 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -147,6 +147,18 @@
}
@Override
+ public void onTextClassifierEvent(@NonNull TextClassifierEvent event) {
+ Preconditions.checkNotNull(event);
+ Utils.checkMainThread();
+
+ try {
+ mManagerService.onTextClassifierEvent(mSessionId, event);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Error reporting textclassifier event.", e);
+ }
+ }
+
+ @Override
public TextLanguage detectLanguage(TextLanguage.Request request) {
Preconditions.checkNotNull(request);
Utils.checkMainThread();
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index ea82bf3..8709e09 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -127,32 +127,34 @@
@StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_EDITTEXT, WIDGET_TYPE_UNSELECTABLE_TEXTVIEW,
WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW,
WIDGET_TYPE_CUSTOM_EDITTEXT, WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW,
- WIDGET_TYPE_UNKNOWN})
+ WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_UNKNOWN})
@interface WidgetType {}
- /** The widget involved in the text classification session is a standard
+ /** The widget involved in the text classification context is a standard
* {@link android.widget.TextView}. */
String WIDGET_TYPE_TEXTVIEW = "textview";
- /** The widget involved in the text classification session is a standard
+ /** The widget involved in the text classification context is a standard
* {@link android.widget.EditText}. */
String WIDGET_TYPE_EDITTEXT = "edittext";
- /** The widget involved in the text classification session is a standard non-selectable
+ /** The widget involved in the text classification context is a standard non-selectable
* {@link android.widget.TextView}. */
String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
- /** The widget involved in the text classification session is a standard
+ /** The widget involved in the text classification context is a standard
* {@link android.webkit.WebView}. */
String WIDGET_TYPE_WEBVIEW = "webview";
- /** The widget involved in the text classification session is a standard editable
+ /** The widget involved in the text classification context is a standard editable
* {@link android.webkit.WebView}. */
String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
- /** The widget involved in the text classification session is a custom text widget. */
+ /** The widget involved in the text classification context is a custom text widget. */
String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
- /** The widget involved in the text classification session is a custom editable text widget. */
+ /** The widget involved in the text classification context is a custom editable text widget. */
String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
- /** The widget involved in the text classification session is a custom non-selectable text
+ /** The widget involved in the text classification context is a custom non-selectable text
* widget. */
String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
- /** The widget involved in the text classification session is of an unknown/unspecified type. */
+ /** The widget involved in the text classification context is a notification */
+ String WIDGET_TYPE_NOTIFICATION = "notification";
+ /** The widget involved in the text classification context is of an unknown/unspecified type. */
String WIDGET_TYPE_UNKNOWN = "unknown";
/**
@@ -349,16 +351,30 @@
@NonNull ConversationActions.Request request) {
Preconditions.checkNotNull(request);
Utils.checkMainThread();
- return new ConversationActions(Collections.emptyList());
+ return new ConversationActions(Collections.emptyList(), null);
}
/**
+ * <strong>NOTE: </strong>Use {@link #onTextClassifierEvent(TextClassifierEvent)} instead.
+ * <p>
* Reports a selection event.
*
* <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*/
- default void onSelectionEvent(@NonNull SelectionEvent event) {}
+ default void onSelectionEvent(@NonNull SelectionEvent event) {
+ // TODO: Consider rerouting to onTextClassifierEvent()
+ }
+
+ /**
+ * Reports a text classifier event.
+ * <p>
+ * <strong>NOTE: </strong>Call on a worker thread.
+ *
+ * @throws IllegalStateException if this TextClassifier has been destroyed.
+ * @see #isDestroyed()
+ */
+ default void onTextClassifierEvent(@NonNull TextClassifierEvent event) {}
/**
* Destroys this TextClassifier.
diff --git a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl b/core/java/android/view/textclassifier/TextClassifierEvent.aidl
similarity index 76%
copy from telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
copy to core/java/android/view/textclassifier/TextClassifierEvent.aidl
index 4c289ac..86fa4f8 100644
--- a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.aidl
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.telephony.rcs;
+package android.view.textclassifier;
-interface IRcs {
- // RcsManager APIs
- void deleteThread(int threadId);
-
- // RcsThread APIs
- int getMessageCount(int rcsThreadId);
-}
\ No newline at end of file
+parcelable TextClassifierEvent;
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
new file mode 100644
index 0000000..3bb9ee8
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -0,0 +1,501 @@
+/*
+ * 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.view.textclassifier;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A text classifier event.
+ */
+// TODO: Comprehensive javadoc.
+public final class TextClassifierEvent implements Parcelable {
+
+ public static final Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() {
+ @Override
+ public TextClassifierEvent createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public TextClassifierEvent[] newArray(int size) {
+ return new TextClassifierEvent[size];
+ }
+ };
+
+ /** @hide **/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CATEGORY_UNDEFINED, CATEGORY_SELECTION, CATEGORY_LINKIFY,
+ CATEGORY_CONVERSATION_ACTIONS, CATEGORY_LANGUAGE_DETECTION})
+ public @interface Category {
+ // For custom event categories, use range 1000+.
+ }
+ /** Undefined category */
+ public static final int CATEGORY_UNDEFINED = 0;
+ /** Smart selection */
+ public static final int CATEGORY_SELECTION = 1;
+ /** Linkify */
+ public static final int CATEGORY_LINKIFY = 2;
+ /** Conversation actions */
+ public static final int CATEGORY_CONVERSATION_ACTIONS = 3;
+ /** Language detection */
+ public static final int CATEGORY_LANGUAGE_DETECTION = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_UNDEFINED, TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED,
+ TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION,
+ TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION,
+ TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION,
+ TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL,
+ TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY})
+ public @interface Type {
+ // For custom event types, use range 1,000,000+.
+ }
+ /** User started a new selection. */
+ public static final int TYPE_UNDEFINED = 0;
+ /** User started a new selection. */
+ public static final int TYPE_SELECTION_STARTED = 1;
+ /** User modified an existing selection. */
+ public static final int TYPE_SELECTION_MODIFIED = 2;
+ /** Smart selection triggered for a single token (word). */
+ public static final int TYPE_SMART_SELECTION_SINGLE = 3;
+ /** Smart selection triggered spanning multiple tokens (words). */
+ public static final int TYPE_SMART_SELECTION_MULTI = 4;
+ /** Something else other than user or the default TextClassifier triggered a selection. */
+ public static final int TYPE_AUTO_SELECTION = 5;
+ /** Smart actions shown to the user. */
+ public static final int TYPE_ACTIONS_SHOWN = 6;
+ /** User clicked a link. */
+ public static final int TYPE_LINK_CLICKED = 7;
+ /** User typed over the selection. */
+ public static final int TYPE_OVERTYPE = 8;
+ /** User clicked on Copy action. */
+ public static final int TYPE_COPY_ACTION = 9;
+ /** User clicked on Paste action. */
+ public static final int TYPE_PASTE_ACTION = 10;
+ /** User clicked on Cut action. */
+ public static final int TYPE_CUT_ACTION = 11;
+ /** User clicked on Share action. */
+ public static final int TYPE_SHARE_ACTION = 12;
+ /** User clicked on a Smart action. */
+ public static final int TYPE_SMART_ACTION = 13;
+ /** User dragged+dropped the selection. */
+ public static final int TYPE_SELECTION_DRAG = 14;
+ /** Selection is destroyed. */
+ public static final int TYPE_SELECTION_DESTROYED = 15;
+ /** User clicked on a custom action. */
+ public static final int TYPE_OTHER_ACTION = 16;
+ /** User clicked on Select All action */
+ public static final int TYPE_SELECT_ALL = 17;
+ /** User reset the smart selection. */
+ public static final int TYPE_SELECTION_RESET = 18;
+ /** User composed a reply. */
+ public static final int TYPE_MANUAL_REPLY = 19;
+
+ @Category private final int mEventCategory;
+ @Type private final int mEventType;
+ @Nullable private final String mEntityType;
+ @Nullable private final TextClassificationContext mEventContext;
+ @Nullable private final String mResultId;
+ private final int mEventIndex;
+ private final long mEventTime;
+ private final Bundle mExtras;
+
+ // Smart selection.
+ private final int mRelativeWordStartIndex;
+ private final int mRelativeWordEndIndex;
+ private final int mRelativeSuggestedWordStartIndex;
+ private final int mRelativeSuggestedWordEndIndex;
+
+ // Smart action.
+ private final int[] mActionIndices;
+
+ // Language detection.
+ @Nullable private final String mLanguage;
+
+ private TextClassifierEvent(
+ int eventCategory,
+ int eventType,
+ String entityType,
+ TextClassificationContext eventContext,
+ String resultId,
+ int eventIndex,
+ long eventTime,
+ Bundle extras,
+ int relativeWordStartIndex,
+ int relativeWordEndIndex,
+ int relativeSuggestedWordStartIndex,
+ int relativeSuggestedWordEndIndex,
+ int[] actionIndex,
+ String language) {
+ mEventCategory = eventCategory;
+ mEventType = eventType;
+ mEntityType = entityType;
+ mEventContext = eventContext;
+ mResultId = resultId;
+ mEventIndex = eventIndex;
+ mEventTime = eventTime;
+ mExtras = extras;
+ mRelativeWordStartIndex = relativeWordStartIndex;
+ mRelativeWordEndIndex = relativeWordEndIndex;
+ mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex;
+ mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
+ mActionIndices = actionIndex;
+ mLanguage = language;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEventCategory);
+ dest.writeInt(mEventType);
+ dest.writeString(mEntityType);
+ dest.writeParcelable(mEventContext, flags);
+ dest.writeString(mResultId);
+ dest.writeInt(mEventIndex);
+ dest.writeLong(mEventTime);
+ dest.writeBundle(mExtras);
+ dest.writeInt(mRelativeWordStartIndex);
+ dest.writeInt(mRelativeWordEndIndex);
+ dest.writeInt(mRelativeSuggestedWordStartIndex);
+ dest.writeInt(mRelativeSuggestedWordEndIndex);
+ dest.writeIntArray(mActionIndices);
+ dest.writeString(mLanguage);
+ }
+
+ private static TextClassifierEvent readFromParcel(Parcel in) {
+ return new TextClassifierEvent(
+ /* eventCategory= */ in.readInt(),
+ /* eventType= */ in.readInt(),
+ /* entityType= */ in.readString(),
+ /* eventContext= */ in.readParcelable(null),
+ /* resultId= */ in.readString(),
+ /* eventIndex= */ in.readInt(),
+ /* eventTime= */ in.readLong(),
+ /* extras= */ in.readBundle(),
+ /* relativeWordStartIndex= */ in.readInt(),
+ /* relativeWordEndIndex= */ in.readInt(),
+ /* relativeSuggestedWordStartIndex= */ in.readInt(),
+ /* relativeSuggestedWordEndIndex= */ in.readInt(),
+ /* actionIndices= */ in.createIntArray(),
+ /* language= */ in.readString());
+ }
+
+ /**
+ * Returns the event category. e.g. {@link #CATEGORY_SELECTION}.
+ */
+ @Category
+ public int getEventCategory() {
+ return mEventCategory;
+ }
+
+ /**
+ * Returns the event type. e.g. {@link #TYPE_SELECTION_STARTED}.
+ */
+ @Type
+ public int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * Returns the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ */
+ @Nullable
+ public String getEntityType() {
+ return mEntityType;
+ }
+
+ /**
+ * Returns the event context.
+ */
+ @Nullable
+ public TextClassificationContext getEventContext() {
+ return mEventContext;
+ }
+
+ /**
+ * Returns the id of the text classifier result related to this event.
+ */
+ @Nullable
+ public String getResultId() {
+ return mResultId;
+ }
+
+ /**
+ * Returns the index of this event in the series of event it belongs to.
+ */
+ public int getEventIndex() {
+ return mEventIndex;
+ }
+
+ /**
+ * Returns the time this event occurred. This is the number of milliseconds since
+ * January 1, 1970, 00:00:00 GMT. 0 indicates not set.
+ */
+ public long getEventTime() {
+ return mEventTime;
+ }
+
+ /**
+ * Returns a bundle containing non-structured extra information about this event.
+ *
+ * <p><b>NOTE: </b>Do not modify this bundle.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * For smart selection. Returns the relative word index of the start of the selection.
+ */
+ public int getRelativeWordStartIndex() {
+ return mRelativeWordStartIndex;
+ }
+
+ /**
+ * For smart selection. Returns the relative word (exclusive) index of the end of the selection.
+ */
+ public int getRelativeWordEndIndex() {
+ return mRelativeWordEndIndex;
+ }
+
+ /**
+ * For smart selection. Returns the relative word index of the start of the smart selection.
+ */
+ public int getRelativeSuggestedWordStartIndex() {
+ return mRelativeSuggestedWordStartIndex;
+ }
+
+ /**
+ * For smart selection. Returns the relative word (exclusive) index of the end of the
+ * smart selection.
+ */
+ public int getRelativeSuggestedWordEndIndex() {
+ return mRelativeSuggestedWordEndIndex;
+ }
+
+ /**
+ * Returns the indices of the actions relating to this event.
+ * Actions are usually returned by the text classifier in priority order with the most
+ * preferred action at index 0. This list gives an indication of the position of the actions
+ * that are being reported.
+ */
+ @NonNull
+ public int[] getActionIndices() {
+ return mActionIndices;
+ }
+
+ /**
+ * For language detection. Returns the language tag for the detected locale.
+ * @see java.util.Locale#forLanguageTag(String).
+ */
+ @Nullable
+ public String getLanguage() {
+ return mLanguage;
+ }
+
+ /**
+ * Builder to build a text classifier event.
+ */
+ public static final class Builder {
+
+ private final int mEventCategory;
+ private final int mEventType;
+ @Nullable private String mEntityType;
+ @Nullable private TextClassificationContext mEventContext;
+ @Nullable private String mResultId;
+ private int mEventIndex;
+ private long mEventTime;
+ @Nullable private Bundle mExtras;
+ private int mRelativeWordStartIndex;
+ private int mRelativeWordEndIndex;
+ private int mRelativeSuggestedWordStartIndex;
+ private int mRelativeSuggestedWordEndIndex;
+ private int[] mActionIndices = new int[0];
+ @Nullable private String mLanguage;
+
+ /**
+ * Creates a builder for building {@link TextClassifierEvent}s.
+ *
+ * @param eventCategory The event category. e.g. {@link #CATEGORY_SELECTION}
+ * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED}
+ */
+ public Builder(@Category int eventCategory, @Type int eventType) {
+ mEventCategory = eventCategory;
+ mEventType = eventType;
+ }
+
+ /**
+ * Sets the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ */
+ @NonNull
+ public Builder setEntityType(@Nullable String entityType) {
+ mEntityType = entityType;
+ return this;
+ }
+
+ /**
+ * Sets the event context.
+ */
+ @NonNull
+ public Builder setEventContext(@Nullable TextClassificationContext eventContext) {
+ mEventContext = eventContext;
+ return this;
+ }
+
+ /**
+ * Sets the id of the text classifier result related to this event.
+ */
+ @NonNull
+ public Builder setResultId(@Nullable String resultId) {
+ mResultId = resultId;
+ return this;
+ }
+
+ /**
+ * Sets the index of this events in the series of events it belongs to.
+ */
+ @NonNull
+ public Builder setEventIndex(int eventIndex) {
+ mEventIndex = eventIndex;
+ return this;
+ }
+
+ /**
+ * Sets the time this event occurred. This is the number of milliseconds since
+ * January 1, 1970, 00:00:00 GMT. 0 indicates not set.
+ */
+ @NonNull
+ public Builder setEventTime(long eventTime) {
+ mEventTime = eventTime;
+ return this;
+ }
+
+ /**
+ * Sets a bundle containing non-structured extra information about the event.
+ *
+ * <p><b>NOTE: </b>Prefer to set only immutable values on the bundle otherwise, avoid
+ * updating the internals of this bundle as it may have unexpected consequences on the
+ * clients of the built event object. For similar reasons, avoid depending on mutable
+ * objects in this bundle.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = Preconditions.checkNotNull(extras);
+ return this;
+ }
+
+ /**
+ * For smart selection. Sets the relative word index of the start of the selection.
+ */
+ @NonNull
+ public Builder setRelativeWordStartIndex(int relativeWordStartIndex) {
+ mRelativeWordStartIndex = relativeWordStartIndex;
+ return this;
+ }
+
+ /**
+ * For smart selection. Sets the relative word (exclusive) index of the end of the
+ * selection.
+ */
+ @NonNull
+ public Builder setRelativeWordEndIndex(int relativeWordEndIndex) {
+ mRelativeWordEndIndex = relativeWordEndIndex;
+ return this;
+ }
+
+ /**
+ * For smart selection. Sets the relative word index of the start of the smart selection.
+ */
+ @NonNull
+ public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) {
+ mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex;
+ return this;
+ }
+
+ /**
+ * For smart selection. Sets the relative word (exclusive) index of the end of the
+ * smart selection.
+ */
+ @NonNull
+ public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) {
+ mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
+ return this;
+ }
+
+ /**
+ * Sets the indices of the actions involved in this event. Actions are usually returned by
+ * the text classifier in priority order with the most preferred action at index 0.
+ * This index gives an indication of the position of the action that is being reported.
+ */
+ @NonNull
+ public Builder setActionIndices(@NonNull int... actionIndices) {
+ mActionIndices = new int[actionIndices.length];
+ System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length);
+ return this;
+ }
+
+ /**
+ * For language detection. Sets the language tag for the detected locale.
+ * @see java.util.Locale#forLanguageTag(String).
+ */
+ @NonNull
+ public Builder setLanguage(@Nullable String language) {
+ mLanguage = language;
+ return this;
+ }
+
+ /**
+ * Builds and returns a text classifier event.
+ */
+ @NonNull
+ public TextClassifierEvent build() {
+ mExtras = mExtras == null ? Bundle.EMPTY : mExtras;
+ return new TextClassifierEvent(
+ mEventCategory,
+ mEventType,
+ mEntityType,
+ mEventContext,
+ mResultId,
+ mEventIndex,
+ mEventTime,
+ mExtras,
+ mRelativeWordStartIndex,
+ mRelativeWordEndIndex,
+ mRelativeSuggestedWordStartIndex,
+ mRelativeSuggestedWordEndIndex,
+ mActionIndices,
+ mLanguage);
+ }
+ // TODO: Add build(boolean validate).
+ }
+}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index deda926..9b0f9c6 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -341,6 +341,11 @@
}
}
+ @Override
+ public void onTextClassifierEvent(@NonNull TextClassifierEvent event) {
+ // TODO: Implement.
+ }
+
/** @inheritDoc */
@Override
public TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
@@ -387,7 +392,10 @@
Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>();
- int maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
+ int maxSuggestions = nativeSuggestions.length;
+ if (request.getMaxSuggestions() > 0) {
+ maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
+ }
for (int i = 0; i < maxSuggestions; i++) {
ActionsSuggestionsModel.ActionSuggestion nativeSuggestion = nativeSuggestions[i];
String actionType = nativeSuggestion.getActionType();
@@ -400,7 +408,7 @@
.setConfidenceScore(nativeSuggestion.getScore())
.build());
}
- return new ConversationActions(conversationActions);
+ return new ConversationActions(conversationActions, /*id*/ null);
} catch (Throwable t) {
// Avoid throwing from this method. Log the error.
Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 90da812..ddf1190 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -719,7 +719,7 @@
@ViewDebug.ExportedProperty(category = "text")
@UnsupportedAppUsage
private int mGravity = Gravity.TOP | Gravity.START;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mHorizontallyScrolling;
private int mAutoLinkMask;
@@ -5095,13 +5095,24 @@
}
/**
- * Returns whether the text is allowed to be wider than the View is.
+ * Returns whether the text is allowed to be wider than the View.
+ * If false, the text will be wrapped to the width of the View.
+ *
+ * @attr ref android.R.styleable#TextView_scrollHorizontally
+ * @see #setHorizontallyScrolling(boolean)
+ */
+ public final boolean isHorizontallyScrolling() {
+ return mHorizontallyScrolling;
+ }
+
+ /**
+ * Returns whether the text is allowed to be wider than the View.
* If false, the text will be wrapped to the width of the View.
*
* @attr ref android.R.styleable#TextView_scrollHorizontally
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public boolean getHorizontallyScrolling() {
return mHorizontallyScrolling;
}
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/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 5465485..051a96c 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -80,7 +80,8 @@
private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
private final Object mLock = new Object();
private final Random mRandom;
- private long mStartTime = System.currentTimeMillis();
+ private long mStartCurrentTime = System.currentTimeMillis();
+ private long mStartElapsedTime = SystemClock.elapsedRealtime();
private long mCallStatsCount = 0;
private boolean mAddDebugEntries = false;
@@ -329,8 +330,8 @@
// Debug entries added to help validate the data.
if (mAddDebugEntries && mBatteryStopwatch != null) {
- resultCallStats.add(createDebugEntry("start_time_millis", mStartTime));
- resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ resultCallStats.add(createDebugEntry("start_time_millis", mStartElapsedTime));
+ resultCallStats.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
resultCallStats.add(
createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
}
@@ -370,7 +371,7 @@
long totalRecordedCallsCount = 0;
long totalCpuTime = 0;
pw.print("Start time: ");
- pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+ pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartCurrentTime));
pw.print("On battery time (ms): ");
pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
@@ -520,7 +521,8 @@
mCallStatsCount = 0;
mUidEntries.clear();
mExceptionCounts.clear();
- mStartTime = System.currentTimeMillis();
+ mStartCurrentTime = System.currentTimeMillis();
+ mStartElapsedTime = SystemClock.elapsedRealtime();
if (mBatteryStopwatch != null) {
mBatteryStopwatch.reset();
}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 01fd8ba..9a7fb9f 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -50,7 +50,8 @@
private int mSamplingInterval;
private CachedDeviceState.Readonly mDeviceState;
private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
- private long mStartTime = System.currentTimeMillis();
+ private long mStartCurrentTime = System.currentTimeMillis();
+ private long mStartElapsedTime = SystemClock.elapsedRealtime();
private boolean mAddDebugEntries = false;
public LooperStats(int samplingInterval, int entriesSizeCap) {
@@ -155,8 +156,8 @@
maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
// Debug entries added to help validate the data.
if (mAddDebugEntries && mBatteryStopwatch != null) {
- exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
- exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ exportedEntries.add(createDebugEntry("start_time_millis", mStartElapsedTime));
+ exportedEntries.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
exportedEntries.add(
createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
}
@@ -173,7 +174,11 @@
/** Returns a timestamp indicating when the statistics were last reset. */
public long getStartTimeMillis() {
- return mStartTime;
+ return mStartCurrentTime;
+ }
+
+ public long getStartElapsedTimeMillis() {
+ return mStartElapsedTime;
}
public long getBatteryTimeMillis() {
@@ -199,7 +204,8 @@
synchronized (mOverflowEntry) {
mOverflowEntry.reset();
}
- mStartTime = System.currentTimeMillis();
+ mStartCurrentTime = System.currentTimeMillis();
+ mStartElapsedTime = SystemClock.elapsedRealtime();
if (mBatteryStopwatch != null) {
mBatteryStopwatch.reset();
}
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/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index c7a6b68..82460ec 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -141,4 +141,7 @@
PM_UNINSTALL = 113;
WIFI_SERVICE_ADD_NETWORK_SUGGESTIONS = 114;
WIFI_SERVICE_ADD_OR_UPDATE_NETWORK = 115;
+ QUERY_SUMMARY_FOR_DEVICE = 116;
+ REMOVE_CROSS_PROFILE_WIDGET_PROVIDER = 117;
+ ESTABLISH_VPN = 118;
}
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/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 84c6446..43b3552 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -59,4 +59,6 @@
<!-- Theme for the dialog shown when an app crashes or ANRs. -->
<style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" />
-</resources>
+
+ <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault" />
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 35263a3..dd51cb6 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1294,6 +1294,20 @@
supports any size. -->
<attr name="maxAspectRatio" format="float" />
+ <!-- This value indicates the minimum aspect ratio the activity supports. If the app runs on a
+ device with a narrower aspect ratio, the system automatically letterboxes the app, leaving
+ portions of the screen unused so the app can run at its specified minimum aspect ratio.
+ <p>
+ Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+ form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.
+ <p>
+ Value needs to be greater or equal to 1.0, otherwise it is ignored.
+ <p>
+ NOTE: This attribute is ignored if the activity has
+ {@link android.R.attr#resizeableActivity} set to true, since that means your activity
+ supports any size. -->
+ <attr name="minAspectRatio" format="float" />
+
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
While in lockTask mode the system will not launch non-permitted tasks until
lockTask mode is disabled.
@@ -1571,6 +1585,7 @@
<attr name="directBootAware" />
<attr name="resizeableActivity" />
<attr name="maxAspectRatio" />
+ <attr name="minAspectRatio" />
<attr name="networkSecurityConfig" />
<!-- Declare the category of this app. Categories are used to cluster multiple apps
together into meaningful groups, such as when summarizing battery, network, or
@@ -2386,6 +2401,7 @@
<attr name="resizeableActivity" />
<attr name="supportsPictureInPicture" />
<attr name="maxAspectRatio" />
+ <attr name="minAspectRatio" />
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 05a5bed..1f9035e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2931,6 +2931,7 @@
<public name="selectionDividerHeight" />
<public name="foregroundServiceType" />
<public name="hasFragileUserData" />
+ <public name="minAspectRatio" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
@@ -2971,6 +2972,8 @@
<public-group type="dimen" first-id="0x01050007">
<!-- @hide @SystemApi -->
<public name="config_restrictedIconSize" />
+ <!-- @hide @SystemApi -->
+ <public name="config_mediaMetadataBitmapMaxSize" />
</public-group>
<!-- ===============================================================
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cab01f9..f25427a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3200,6 +3200,8 @@
<!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
<string name="android_start_title" product="default">Phone is starting\u2026</string>
<!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
+ <string name="android_start_title" product="automotive">Android is starting\u2026</string>
+ <!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
<string name="android_start_title" product="tablet">Tablet is starting\u2026</string>
<!-- [CHAR LIMIT=40] Title of dialog that is shown when system is starting. -->
<string name="android_start_title" product="device">Device is starting\u2026</string>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fec101a..c0c677a 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1646,8 +1646,10 @@
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
+ <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" />
+
<!-- Theme used for the intent picker activity. -->
- <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light">
+ <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.DayNight">
<item name="windowEnterTransition">@empty</item>
<item name="windowExitTransition">@empty</item>
<item name="windowIsTranslucent">true</item>
@@ -1659,30 +1661,8 @@
<item name="colorControlActivated">?attr/colorControlHighlight</item>
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
-
- <!-- Dialog attributes -->
- <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
- <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
-
- <!-- Button styles -->
- <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
- <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- <item name="colorError">@color/error_color_device_default_light</item>
-
- <!-- Progress bar attributes -->
- <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
- <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
-
- <!-- Toolbar attributes -->
- <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
</style>
-
<!-- @hide DeviceDefault themes for the autofill FillUi -->
<style name="Theme.DeviceDefault.Autofill" />
<style name="Theme.DeviceDefault.Light.Autofill" />
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/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 9b5c034..81ec85e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -18,7 +18,6 @@
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -375,7 +374,7 @@
ConversationActions.Message message =
new ConversationActions.Message.Builder(
ConversationActions.Message.PERSON_USER_REMOTE)
- .setText("Hello")
+ .setText("Where are you?")
.build();
ConversationActions.TypeConfig typeConfig =
new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
@@ -384,19 +383,44 @@
.build();
ConversationActions.Request request =
new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(3)
+ .setMaxSuggestions(1)
.setTypeConfig(typeConfig)
.build();
ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
assertTrue(conversationActions.getConversationActions().size() > 0);
- assertTrue(conversationActions.getConversationActions().size() <= 3);
+ assertTrue(conversationActions.getConversationActions().size() == 1);
for (ConversationActions.ConversationAction conversationAction :
conversationActions.getConversationActions()) {
- assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
- assertNotNull(conversationAction.getTextReply());
- assertTrue(conversationAction.getConfidenceScore() > 0);
- assertTrue(conversationAction.getConfidenceScore() <= 1);
+ assertThat(conversationAction,
+ isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+ }
+ }
+
+ @Test
+ public void testSuggestConversationActions_textReplyOnly_noMax() {
+ if (isTextClassifierDisabled()) return;
+ ConversationActions.Message message =
+ new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_REMOTE)
+ .setText("Where are you?")
+ .build();
+ ConversationActions.TypeConfig typeConfig =
+ new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+ .setIncludedTypes(
+ Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+ .build();
+ ConversationActions.Request request =
+ new ConversationActions.Request.Builder(Collections.singletonList(message))
+ .setTypeConfig(typeConfig)
+ .build();
+
+ ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
+ assertTrue(conversationActions.getConversationActions().size() > 1);
+ for (ConversationActions.ConversationAction conversationAction :
+ conversationActions.getConversationActions()) {
+ assertThat(conversationAction,
+ isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
}
}
@@ -498,4 +522,36 @@
}
};
}
+
+ private static Matcher<ConversationActions.ConversationAction> isConversationAction(
+ String actionType) {
+ return new BaseMatcher<ConversationActions.ConversationAction>() {
+ @Override
+ public boolean matches(Object o) {
+ if (!(o instanceof ConversationActions.ConversationAction)) {
+ return false;
+ }
+ ConversationActions.ConversationAction conversationAction =
+ (ConversationActions.ConversationAction) o;
+ if (!actionType.equals(conversationAction.getType())) {
+ return false;
+ }
+ if (ConversationActions.TYPE_TEXT_REPLY.equals(actionType)) {
+ if (conversationAction.getTextReply() == null) {
+ return false;
+ }
+ }
+ if (conversationAction.getConfidenceScore() < 0
+ || conversationAction.getConfidenceScore() > 1) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("actionType=").appendValue(actionType);
+ }
+ };
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 97f02cb..dc3a12f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import android.os.Binder;
+import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -637,7 +638,7 @@
@Test
public void testAddsDebugEntries() {
- long startTime = System.currentTimeMillis();
+ long startTime = SystemClock.elapsedRealtime();
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setAddDebugEntries(true);
ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index f26dfad..b65c1e6 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -442,11 +442,13 @@
LooperStats.ExportedEntry debugEntry1 = entries.get(1);
assertThat(debugEntry1.handlerClassName).isEqualTo("");
assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
- assertThat(debugEntry1.totalLatencyMicros).isEqualTo(looperStats.getStartTimeMillis());
+ assertThat(debugEntry1.totalLatencyMicros).isEqualTo(
+ looperStats.getStartElapsedTimeMillis());
LooperStats.ExportedEntry debugEntry2 = entries.get(2);
assertThat(debugEntry2.handlerClassName).isEqualTo("");
assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
- assertThat(debugEntry2.totalLatencyMicros).isAtLeast(looperStats.getStartTimeMillis());
+ assertThat(debugEntry2.totalLatencyMicros).isAtLeast(
+ looperStats.getStartElapsedTimeMillis());
LooperStats.ExportedEntry debugEntry3 = entries.get(3);
assertThat(debugEntry3.handlerClassName).isEqualTo("");
assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
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/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d22eaf3..0503f36 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -60,6 +60,7 @@
"libutils",
"libEGL",
"libGLESv2",
+ "libGLESv3",
"libvulkan",
"libui",
"libgui",
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/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
new file mode 100644
index 0000000..8d48a58
--- /dev/null
+++ b/native/webview/plat_support/draw_fn.h
@@ -0,0 +1,197 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+//******************************************************************************
+// This is a copy of the coresponding android_webview/public/browser header.
+// Any changes to the interface should be made there as well.
+//******************************************************************************
+
+#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
+#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
+
+#include <vulkan/vulkan.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// In order to make small changes backwards compatible, all structs passed from
+// android to chromium are versioned.
+//
+// 1 is Android Q. This matches kAwDrawGLInfoVersion version 3.
+static const int kAwDrawFnVersion = 1;
+
+struct AwDrawFn_OnSyncParams {
+ int version;
+
+ bool apply_force_dark;
+};
+
+struct AwDrawFn_DrawGLParams {
+ int version;
+
+ // Input: current clip rect in surface coordinates. Reflects the current state
+ // of the OpenGL scissor rect. Both the OpenGL scissor rect and viewport are
+ // set by the caller of the draw function and updated during View animations.
+ int clip_left;
+ int clip_top;
+ int clip_right;
+ int clip_bottom;
+
+ // Input: current width/height of destination surface.
+ int width;
+ int height;
+
+ // Input: is the View rendered into an independent layer.
+ // If false, the surface is likely to hold to the full screen contents, with
+ // the scissor box set by the caller to the actual View location and size.
+ // Also the transformation matrix will contain at least a translation to the
+ // position of the View to render, plus any other transformations required as
+ // part of any ongoing View animation. View translucency (alpha) is ignored,
+ // although the framework will set is_layer to true for non-opaque cases.
+ // Can be requested via the View.setLayerType(View.LAYER_TYPE_NONE, ...)
+ // Android API method.
+ //
+ // If true, the surface is dedicated to the View and should have its size.
+ // The viewport and scissor box are set by the caller to the whole surface.
+ // Animation transformations are handled by the caller and not reflected in
+ // the provided transformation matrix. Translucency works normally.
+ // Can be requested via the View.setLayerType(View.LAYER_TYPE_HARDWARE, ...)
+ // Android API method.
+ bool is_layer;
+
+ // Input: current transformation matrix in surface pixels.
+ // Uses the column-based OpenGL matrix format.
+ float transform[16];
+};
+
+struct AwDrawFn_InitVkParams {
+ int version;
+ VkInstance instance;
+ VkPhysicalDevice physical_device;
+ VkDevice device;
+ VkQueue queue;
+ uint32_t graphics_queue_index;
+ uint32_t instance_version;
+ const char* const* enabled_extension_names;
+ // Only one of device_features and device_features_2 should be non-null.
+ // If both are null then no features are enabled.
+ VkPhysicalDeviceFeatures* device_features;
+ VkPhysicalDeviceFeatures2* device_features_2;
+};
+
+struct AwDrawFn_DrawVkParams {
+ int version;
+
+ // Input: current width/height of destination surface.
+ int width;
+ int height;
+
+ // Input: is the render target a FBO
+ bool is_layer;
+
+ // Input: current transform matrix
+ float transform[16];
+
+ // Input WebView should do its main compositing draws into this. It cannot do
+ // anything that would require stopping the render pass.
+ VkCommandBuffer secondary_command_buffer;
+
+ // Input: The main color attachment index where secondary_command_buffer will
+ // eventually be submitted.
+ uint32_t color_attachment_index;
+
+ // Input: A render pass which will be compatible to the one which the
+ // secondary_command_buffer will be submitted into.
+ VkRenderPass compatible_render_pass;
+
+ // Input: Format of the destination surface.
+ VkFormat format;
+
+ // Input: Color space transformation from linear RGB to D50-adapted XYZ
+ float matrix[9];
+
+ // Input: current clip rect
+ int clip_left;
+ int clip_top;
+ int clip_right;
+ int clip_bottom;
+};
+
+struct AwDrawFn_PostDrawVkParams {
+ int version;
+
+ // Input: Fence for the composite command buffer to signal it has finished its
+ // work on the GPU.
+ int fd;
+};
+
+// Called on render thread while UI thread is blocked. Called for both GL and
+// VK.
+typedef void AwDrawFn_OnSync(int functor, AwDrawFn_OnSyncParams* params);
+
+// Called on render thread when either the context is destroyed _or_ when the
+// functor's last reference goes away. Will always be called with an active
+// context. Called for both GL and VK.
+typedef void AwDrawFn_OnContextDestroyed(int functor);
+
+// Called on render thread when the last reference to the handle goes away and
+// the handle is considered irrevocably destroyed. Will always be proceeded by
+// a call to OnContextDestroyed if this functor had ever been drawn. Called for
+// both GL and VK.
+typedef void AwDrawFn_OnDestroyed(int functor);
+
+// Only called for GL.
+typedef void AwDrawFn_DrawGL(int functor, AwDrawFn_DrawGLParams* params);
+
+// Initialize vulkan state. Needs to be called again after any
+// OnContextDestroyed. Only called for Vulkan.
+typedef void AwDrawFn_InitVk(int functor, AwDrawFn_InitVkParams* params);
+
+// Only called for Vulkan.
+typedef void AwDrawFn_DrawVk(int functor, AwDrawFn_DrawVkParams* params);
+
+// Only called for Vulkan.
+typedef void AwDrawFn_PostDrawVk(int functor,
+ AwDrawFn_PostDrawVkParams* params);
+
+struct AwDrawFnFunctorCallbacks {
+ // No version here since this is passed from chromium to android.
+ AwDrawFn_OnSync* on_sync;
+ AwDrawFn_OnContextDestroyed* on_context_destroyed;
+ AwDrawFn_OnDestroyed* on_destroyed;
+ AwDrawFn_DrawGL* draw_gl;
+ AwDrawFn_InitVk* init_vk;
+ AwDrawFn_DrawVk* draw_vk;
+ AwDrawFn_PostDrawVk* post_draw_vk;
+};
+
+enum AwDrawFnRenderMode {
+ AW_DRAW_FN_RENDER_MODE_OPENGL_ES = 0,
+ AW_DRAW_FN_RENDER_MODE_VULKAN = 1,
+};
+
+// Get the render mode. Result is static for the process.
+typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
+
+// Create a functor. |functor_callbacks| should be valid until OnDestroyed.
+typedef int AwDrawFn_CreateFunctor(AwDrawFnFunctorCallbacks* functor_callbacks);
+
+// May be called on any thread to signal that the functor should be destroyed.
+// The functor will receive an onDestroyed when the last usage of it is
+// released, and it should be considered alive & active until that point.
+typedef void AwDrawFn_ReleaseFunctor(int functor);
+
+struct AwDrawFnFunctionTable {
+ int version;
+ AwDrawFn_QueryRenderMode* query_render_mode;
+ AwDrawFn_CreateFunctor* create_functor;
+ AwDrawFn_ReleaseFunctor* release_functor;
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
diff --git a/native/webview/plat_support/draw_vk_functor.cpp b/native/webview/plat_support/draw_vk_functor.cpp
index 1ba559d..eab1340 100644
--- a/native/webview/plat_support/draw_vk_functor.cpp
+++ b/native/webview/plat_support/draw_vk_functor.cpp
@@ -20,6 +20,7 @@
#define LOG_TAG "webviewchromium_plat_support"
+#include "draw_fn.h"
#include "draw_vk.h"
#include <jni.h>
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/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 1737b64..85dab57 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -107,36 +107,44 @@
private CarAudioManager mCarAudioManager;
private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
new CarAudioManager.CarVolumeCallback() {
- @Override
- public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
- // TODO: Include zoneId into consideration.
- // For instance
- // - single display + single-zone, ignore zoneId
- // - multi-display + single-zone, zoneId is fixed, may show volume bar on all displays
- // - single-display + multi-zone, may show volume bar on primary display only
- // - multi-display + multi-zone, may show volume bar on display specified by zoneId
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- int value = getSeekbarValue(mCarAudioManager, groupId);
- // Do not update the progress if it is the same as before. When car audio manager sets
- // its group volume caused by the seekbar progress changed, it also triggers this
- // callback. Updating the seekbar at the same time could block the continuous seeking.
- if (value != volumeItem.progress) {
- volumeItem.listItem.setProgress(value);
- volumeItem.progress = value;
- }
- if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
- mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
- }
- }
+ @Override
+ public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
+ // TODO: Include zoneId into consideration.
+ // For instance
+ // - single display + single-zone, ignore zoneId
+ // - multi-display + single-zone, zoneId is fixed, may show volume bar on all
+ // displays
+ // - single-display + multi-zone, may show volume bar on primary display only
+ // - multi-display + multi-zone, may show volume bar on display specified by
+ // zoneId
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ int value = getSeekbarValue(mCarAudioManager, groupId);
+ // Do not update the progress if it is the same as before. When car audio
+ // manager sets
+ // its group volume caused by the seekbar progress changed, it also triggers
+ // this
+ // callback. Updating the seekbar at the same time could block the continuous
+ // seeking.
+ if (value != volumeItem.progress) {
+ volumeItem.listItem.setProgress(value);
+ volumeItem.progress = value;
+ }
+ if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+ mHandler.obtainMessage(H.SHOW,
+ Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
+ }
+ }
- @Override
- public void onMasterMuteChanged(int zoneId, int flags) {
- // ignored
- }
- };
+ @Override
+ public void onMasterMuteChanged(int zoneId, int flags) {
+ // ignored
+ }
+ };
private boolean mHovering;
private boolean mShowing;
private boolean mExpanded;
+ private View mExpandIcon;
+ private VolumeItem mDefaultVolumeItem;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -151,10 +159,8 @@
mAvailableVolumeItems.add(volumeItem);
// The first one is the default item.
if (groupId == 0) {
- volumeItem.defaultItem = true;
- addSeekbarListItem(volumeItem, groupId,
- R.drawable.car_ic_keyboard_arrow_down,
- new ExpandIconListener());
+ mDefaultVolumeItem = volumeItem;
+ setupDefaultListItem();
}
}
@@ -178,6 +184,13 @@
}
};
+ private void setupDefaultListItem() {
+ mDefaultVolumeItem.defaultItem = true;
+ addSeekbarListItem(mDefaultVolumeItem, /* volumeGroupId = */0,
+ R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()
+ );
+ }
+
public CarVolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
@@ -294,7 +307,9 @@
return;
}
mShowing = true;
-
+ if (mVolumeLineItems.isEmpty()) {
+ setupDefaultListItem();
+ }
mDialog.show();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
}
@@ -340,6 +355,13 @@
}
mDialog.dismiss();
mShowing = false;
+ mShowing = false;
+ // if mExpandIcon is null that means user never clicked on the expanded arrow
+ // which implies that the dialog is still not expanded. In that case we do
+ // not want to reset the state
+ if (mExpandIcon != null && mExpanded) {
+ toggleDialogExpansion(/* isClicked = */ false);
+ }
}, DISMISS_DELAY_IN_MILLIS))
.start();
@@ -517,52 +539,62 @@
}
private final class ExpandIconListener implements View.OnClickListener {
-
@Override
public void onClick(final View v) {
- mExpanded = !mExpanded;
- Animator inAnimator;
- if (mExpanded) {
- for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
- // Adding the items which are not coming from the default item.
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- if (volumeItem.defaultItem) {
- // Set progress here due to the progress of seekbar may not be updated.
- volumeItem.listItem.setProgress(volumeItem.progress);
- } else {
- addSeekbarListItem(volumeItem, groupId, 0, null);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_up);
- } else {
- // Only keeping the default stream if it is not expended.
- Iterator itr = mVolumeLineItems.iterator();
- while (itr.hasNext()) {
- SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
- VolumeItem volumeItem = findVolumeItem(seekbarListItem);
- if (!volumeItem.defaultItem) {
- itr.remove();
- } else {
- // Set progress here due to the progress of seekbar may not be updated.
- seekbarListItem.setProgress(volumeItem.progress);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_down);
- }
-
- Animator outAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_out);
- inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
- AnimatorSet animators = new AnimatorSet();
- animators.playTogether(outAnimator, inAnimator);
- animators.setTarget(v);
- animators.start();
- mPagedListAdapter.notifyDataSetChanged();
+ mExpandIcon = v;
+ toggleDialogExpansion(true);
}
}
+ private void toggleDialogExpansion(boolean isClicked) {
+ mExpanded = !mExpanded;
+ Animator inAnimator;
+ if (mExpanded) {
+ for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+ // Adding the items which are not coming from the default item.
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ if (volumeItem.defaultItem) {
+ // Set progress here due to the progress of seekbar may not be updated.
+ volumeItem.listItem.setProgress(volumeItem.progress);
+ } else {
+ addSeekbarListItem(volumeItem, groupId, 0, null);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_up);
+
+ } else {
+ // Only keeping the default stream if it is not expended.
+ Iterator itr = mVolumeLineItems.iterator();
+ while (itr.hasNext()) {
+ SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+ VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+ if (!volumeItem.defaultItem) {
+ itr.remove();
+ } else {
+ // Set progress here due to the progress of seekbar may not be updated.
+ seekbarListItem.setProgress(volumeItem.progress);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_down);
+ }
+
+ Animator outAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_out);
+ inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+ AnimatorSet animators = new AnimatorSet();
+ animators.playTogether(outAnimator, inAnimator);
+ if (!isClicked) {
+ // Do not animate when the state is called to reset the dialogs view and not clicked
+ // by user.
+ animators.setDuration(0);
+ }
+ animators.setTarget(mExpandIcon);
+ animators.start();
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+
private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
private final int mVolumeGroupId;
@@ -601,4 +633,4 @@
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 4d9aaec..f116546 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -31,7 +31,8 @@
<application
android:label="@string/app_name"
android:directBootAware="true"
- android:usesCleartextTraffic="true">
+ android:usesCleartextTraffic="true"
+ android:icon="@mipmap/ic_launcher_android">
<receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver">
<intent-filter>
<action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
@@ -45,6 +46,7 @@
<activity
android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
android:label="@string/action_bar_label"
+ android:exported="true"
android:theme="@style/AppTheme"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
diff --git a/packages/CarrierDefaultApp/res/mipmap/ic_launcher_android.png b/packages/CarrierDefaultApp/res/mipmap/ic_launcher_android.png
new file mode 100644
index 0000000..2e9b196
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/mipmap/ic_launcher_android.png
Binary files differ
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 4f67350..f36b4aa 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -195,19 +195,7 @@
if (success) {
// Trigger re-evaluation upon success http response code
CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, getIntent(),
- getApplicationContext());
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, getIntent(),
- getApplicationContext());
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, getIntent(),
- getApplicationContext());
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER, getIntent(),
- getApplicationContext());
- CarrierActionUtils.applyCarrierAction(
- CarrierActionUtils.CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL, getIntent(),
+ CarrierActionUtils.CARRIER_ACTION_RESET_ALL, getIntent(),
getApplicationContext());
}
finishAndRemoveTask();
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 4518d79..3258d57 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -56,6 +56,7 @@
public static final int CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER = 8;
public static final int CARRIER_ACTION_REGISTER_DEFAULT_NETWORK_AVAIL = 9;
public static final int CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL = 10;
+ public static final int CARRIER_ACTION_RESET_ALL = 11;
public static void applyCarrierAction(int actionIdx, Intent intent, Context context) {
switch (actionIdx) {
@@ -92,6 +93,9 @@
case CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL:
onDeregisterDefaultNetworkAvail(intent, context);
break;
+ case CARRIER_ACTION_RESET_ALL:
+ onResetAllCarrierActions(intent, context);
+ break;
default:
loge("unsupported carrier action index: " + actionIdx);
}
@@ -196,6 +200,14 @@
context.getSystemService(NotificationManager.class).cancelAll();
}
+ private static void onResetAllCarrierActions(Intent intent, Context context) {
+ int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ logd("onResetAllCarrierActions subId: " + subId);
+ final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+ telephonyMgr.carrierActionResetAll(subId);
+ }
+
private static Notification getNotification(Context context, int titleId, int textId,
PendingIntent pendingIntent) {
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
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/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index 60d31fc..0352ebc 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -75,7 +75,7 @@
mContext.getSystemService(TextClassificationManager.class)
.setTextClassifier(mTextClassifier);
when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(new ConversationActions(Collections.emptyList()));
+ .thenReturn(new ConversationActions(Collections.emptyList(), null));
when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification);
// The notification is eligible to have smart suggestions.
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/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index e518e0b..cd3fb0c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -1,5 +1,5 @@
android_library {
- name: "ActionButtonsPreference",
+ name: "SettingsLibActionButtonsPreference",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 0126e7e5..042808a0 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,8 +16,8 @@
"SettingsLibAppPreference",
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
- "SettingsLayoutPreference",
- "ActionButtonsPreference",
+ "SettingsLibLayoutPreference",
+ "SettingsLibActionButtonsPreference",
"SettingsLibEntityHeaderWidgets",
],
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
similarity index 83%
rename from packages/SettingsLib/SettingsLayoutPreference/Android.bp
rename to packages/SettingsLib/LayoutPreference/Android.bp
index 489d360..a1f9a76 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -1,5 +1,5 @@
android_library {
- name: "SettingsLayoutPreference",
+ name: "SettingsLibLayoutPreference",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/LayoutPreference/AndroidManifest.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
rename to packages/SettingsLib/LayoutPreference/AndroidManifest.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/LayoutPreference/res/layout/layout_preference_frame.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
rename to packages/SettingsLib/LayoutPreference/res/layout/layout_preference_frame.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
similarity index 98%
rename from packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
rename to packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
index 01d9c00..e27ae7d 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/LayoutPreference/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/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
rename to packages/SettingsLib/LayoutPreference/res/values/styles.xml
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
similarity index 100%
rename from packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
rename to packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
index 4a8ef1e..924eb04 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
@@ -36,12 +36,12 @@
}
@Implementation
- public static int getCurrentUser() {
+ protected static int getCurrentUser() {
return sCurrentUserId;
}
@Implementation
- public boolean switchUser(int userId) {
+ protected boolean switchUser(int userId) {
mUserSwitchedTo = userId;
return true;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index cae74c8..906dba4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -32,7 +32,7 @@
private BluetoothProfile.ServiceListener mServiceListener;
@Implementation
- public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
+ protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile) {
mServiceListener = listener;
return true;
@@ -43,7 +43,7 @@
}
@Implementation
- public List<Integer> getSupportedProfiles() {
+ protected List<Integer> getSupportedProfiles() {
return mSupportedProfiles;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
index 3e91641..d8fc861 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
@@ -34,7 +34,7 @@
}
@Implementation
- public static String getDefaultDialerApplication(Context context) {
+ protected static String getDefaultDialerApplication(Context context) {
return sDefaultDialer;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
index dd7b007..c8c4526 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
@@ -36,7 +36,7 @@
}
@Implementation
- public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
+ protected static ComponentName getDefaultSmsApplication(Context context, boolean update) {
return sDefaultSmsApplication;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a81e395..c50d646 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -21,11 +21,8 @@
import android.content.pm.UserInfo;
import android.os.UserManager;
-import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@@ -56,5 +53,4 @@
protected List<UserInfo> getProfiles(@UserIdInt int userHandle) {
return getProfiles();
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java
deleted file mode 100644
index 3455765..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowXmlUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.settingslib.testutils.shadow;
-
-import static org.robolectric.shadow.api.Shadow.directlyOn;
-
-import com.android.internal.util.XmlUtils;
-
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.util.ReflectionHelpers;
-
-@Implements(XmlUtils.class)
-public class ShadowXmlUtils {
-
- @Implementation
- public static final int convertValueToInt(CharSequence charSeq, int defaultValue) {
- final Class<?> xmlUtilsClass = ReflectionHelpers.loadClass(
- Robolectric.class.getClassLoader(), "com.android.internal.util.XmlUtils");
- try {
- return directlyOn(xmlUtilsClass, "convertValueToInt",
- ReflectionHelpers.ClassParameter.from(CharSequence.class, charSeq),
- ReflectionHelpers.ClassParameter.from(int.class, new Integer(defaultValue)));
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index 9a169d2..83cc39a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -78,8 +78,8 @@
@Test
public void getForegroundUserInfo() {
ShadowActivityManager.setCurrentUser(17);
- when(mUserManager.getUserInfo(ShadowActivityManager.getCurrentUser()))
- .thenReturn(createUserInfoForId(ShadowActivityManager.getCurrentUser()));
+ when(mUserManager.getUserInfo(ActivityManager.getCurrentUser()))
+ .thenReturn(createUserInfoForId(ActivityManager.getCurrentUser()));
assertThat(mHelper.getForegroundUserInfo().id).isEqualTo(17);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
index 88fef08..97de7ef 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -18,9 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 75492f3..b73c706 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -44,7 +44,6 @@
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
- "androidx.car_car",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
"androidx.preference_preference",
@@ -67,8 +66,6 @@
libs: [
"telephony-common",
- "android.car",
- "android.car.userlib",
],
aaptflags: [
@@ -98,7 +95,6 @@
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
- "androidx.car_car",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
"androidx.preference_preference",
@@ -123,8 +119,6 @@
libs: [
"android.test.runner",
"telephony-common",
- "android.car",
- "android.car.userlib",
"android.test.base",
],
aaptflags: [
@@ -149,8 +143,6 @@
libs: [
"telephony-common",
- "android.car",
- "android.car.userlib",
],
dxflags: ["--multi-dex"],
@@ -186,8 +178,6 @@
],
libs: [
"telephony-common",
- "android.car",
- "android.car.userlib",
],
srcs: [
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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9ccdf79..b133346 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -406,24 +406,6 @@
}
@Override
- public void onPhoneStateChanged(int phoneState) {
- synchronized (KeyguardViewMediator.this) {
- if (TelephonyManager.CALL_STATE_IDLE == phoneState // call ending
- && !mDeviceInteractive // screen off
- && mExternallyEnabled) { // not disabled by any app
-
- // note: this is a way to gracefully reenable the keyguard when the call
- // ends and the screen is off without always reenabling the keyguard
- // each time the screen turns off while in call (and having an occasional ugly
- // flicker while turning back on the screen and disabling the keyguard again).
- if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
- + "keyguard is showing");
- doKeyguardLocked(null);
- }
- }
- }
-
- @Override
public void onClockVisibilityChanged() {
adjustStatusBarLocked();
}
@@ -1316,15 +1298,7 @@
if (!mExternallyEnabled) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
- // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
- // for an occasional ugly flicker in this situation:
- // 1) receive a call with the screen on (no keyguard) or make a call
- // 2) screen times out
- // 3) user hits key to turn screen back on
- // instead, we reenable the keyguard when we know the screen is off and the call
- // ends (see the broadcast receiver below)
- // TODO: clean this up when we have better support at the window manager level
- // for apps that wish to be on top of the keyguard
+ mNeedToReshowWhenReenabled = true;
return;
}
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/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index dc3a607..0702f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -437,7 +437,7 @@
}
row.showAppOpsIcons(entry.mActiveAppOps);
- row.setAudiblyAlerted(entry.audiblyAlerted);
+ row.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs);
}
Trace.beginSection("NotificationPresenter#onUpdateRowStates");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index f543b46..ae9f323 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -103,7 +103,7 @@
public String key;
public StatusBarNotification notification;
public NotificationChannel channel;
- public boolean audiblyAlerted;
+ public long lastAudiblyAlertedMs;
public boolean noisy;
public int importance;
public StatusBarIconView icon;
@@ -172,7 +172,7 @@
public void populateFromRanking(@NonNull Ranking ranking) {
channel = ranking.getChannel();
- audiblyAlerted = ranking.audiblyAlerted();
+ lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
importance = ranking.getImportance();
snoozeCriteria = ranking.getSnoozeCriteria();
userSentiment = ranking.getUserSentiment();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 16a3849..5e244f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -32,6 +32,7 @@
import android.database.ContentObserver;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -96,6 +97,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
@@ -110,6 +112,8 @@
private static final boolean ENABLE_HEADS_UP = true;
private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
+
private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
@@ -139,6 +143,9 @@
private NotificationListener mNotificationListener;
private ShadeController mShadeController;
+ private final Handler mDeferredNotificationViewUpdateHandler;
+ private Runnable mUpdateNotificationViewsCallback;
+
protected IDreamManager mDreamManager;
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
@@ -259,6 +266,7 @@
mBubbleController.setDismissListener(this /* bubbleEventListener */);
mNotificationData = new NotificationData();
Dependency.get(InitController.class).addPostInitTask(this::onPostInit);
+ mDeferredNotificationViewUpdateHandler = new Handler();
}
private void onPostInit() {
@@ -301,6 +309,7 @@
NotificationListContainer listContainer, Callback callback,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
+ mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
mCallback = callback;
mHeadsUpManager = headsUpManager;
mNotificationData.setHeadsUpManager(mHeadsUpManager);
@@ -540,6 +549,17 @@
tagForeground(shadeEntry.notification);
updateNotifications();
mCallback.onNotificationAdded(shadeEntry);
+
+ maybeScheduleUpdateNotificationViews(shadeEntry);
+ }
+
+ private void maybeScheduleUpdateNotificationViews(NotificationData.Entry entry) {
+ long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS
+ - (System.currentTimeMillis() - entry.lastAudiblyAlertedMs);
+ if (audibleAlertTimeout > 0) {
+ mDeferredNotificationViewUpdateHandler.postDelayed(
+ mUpdateNotificationViewsCallback, audibleAlertTimeout);
+ }
}
/**
@@ -937,6 +957,8 @@
}
mCallback.onNotificationUpdated(notification);
+
+ maybeScheduleUpdateNotificationViews(entry);
}
@Override
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..91d08ff 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
@@ -87,6 +87,7 @@
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
@@ -121,6 +122,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 +589,10 @@
updateIconVisibilities();
updateShelfIconColor();
updateRippleAllowed();
+ if (mUpdateBackgroundOnUpdate) {
+ mUpdateBackgroundOnUpdate = false;
+ updateBackgroundColors();
+ }
}
/** Called when the notification's ranking was changed (but nothing else changed). */
@@ -1212,9 +1218,8 @@
}
}
- @Override
public void onUiModeChanged() {
- super.onUiModeChanged();
+ mUpdateBackgroundOnUpdate = true;
reInflateViews();
if (mChildrenContainer != null) {
for (ExpandableNotificationRow child : mChildrenContainer.getNotificationChildren()) {
@@ -1685,14 +1690,17 @@
mPublicLayout.showAppOpsIcons(activeOps);
}
- /** Sets whether the notification being displayed audibly alerted the user. */
- public void setAudiblyAlerted(boolean audiblyAlerted) {
+ /** Sets the last time the notification being displayed audibly alerted the user. */
+ public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
if (NotificationUtils.useNewInterruptionModel(mContext)) {
+ boolean recentlyAudiblyAlerted = System.currentTimeMillis() - lastAudiblyAlertedMs
+ < NotificationEntryManager.RECENTLY_ALERTED_THRESHOLD_MS;
if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
- mChildrenContainer.getHeaderView().setAudiblyAlerted(audiblyAlerted);
+ mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(
+ recentlyAudiblyAlerted);
}
- mPrivateLayout.setAudiblyAlerted(audiblyAlerted);
- mPublicLayout.setAudiblyAlerted(audiblyAlerted);
+ mPrivateLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
+ mPublicLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index edd54ca..6bc39c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1615,15 +1615,15 @@
}
/** Sets whether the notification being displayed audibly alerted the user. */
- public void setAudiblyAlerted(boolean audiblyAlerted) {
+ public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
- mContractedWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
+ mContractedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
}
if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
- mExpandedWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
+ mExpandedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
}
if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
- mHeadsUpWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
+ mHeadsUpWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
}
}
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/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 80c4eb0..5906dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -26,6 +26,9 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class DarkIconDispatcherImpl implements DarkIconDispatcher {
private final LightBarTransitionsController mTransitionsController;
@@ -74,7 +77,7 @@
}
/**
- * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area.
+ * Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
*
* @param darkArea the area in which icons should change it's tint, in logical screen
* coordinates
@@ -103,4 +106,12 @@
mReceivers.valueAt(i).onDarkChanged(mTintArea, mDarkIntensity, mIconTint);
}
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("DarkIconDispatcher: ");
+ pw.println(" mIconTint: 0x" + Integer.toHexString(mIconTint));
+ pw.println(" mDarkIntensity: " + mDarkIntensity + "f");
+ pw.println(" mTintArea: " + mTintArea);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index e0c5516..67b077e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -259,6 +259,10 @@
pw.println();
+ if (mStatusBarIconController != null) {
+ mStatusBarIconController.dump(fd, pw, args);
+ }
+
LightBarTransitionsController transitionsController =
mStatusBarIconController.getTransitionsController();
if (transitionsController != null) {
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/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
index 945ed76..0823db9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
@@ -19,9 +19,17 @@
import android.view.View;
import android.widget.ImageView;
+import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
-public interface DarkIconDispatcher {
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Dispatches events to {@link DarkReceiver}s about changes in darkness, tint area and dark
+ * intensity
+ */
+public interface DarkIconDispatcher extends Dumpable {
void setIconsDarkArea(Rect r);
LightBarTransitionsController getTransitionsController();
@@ -37,6 +45,11 @@
// addDarkReceiver.
void applyDark(DarkReceiver object);
+ /**
+ * Dumpable interface
+ */
+ default void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
+
int DEFAULT_ICON_TINT = Color.WHITE;
Rect sTmpRect = new Rect();
int[] sTmpInt2 = new int[2];
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index 8e88ed0..def7513 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -475,14 +475,14 @@
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
- false, false, null, null);
+ -1, false, null, null);
} else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), 255,
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false,
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), true, -1,
false, null, null);
} else {
outRanking.populate(key, outRanking.getRank(),
@@ -490,7 +490,7 @@
outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), false, false,
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), false, -1,
false, null, null);
}
return true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 8706e21..904e5b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -167,7 +167,7 @@
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false, false, false, null, null);
+ null, null, null, true, sentiment, false, -1, false, null, null);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
@@ -185,7 +185,7 @@
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
null, null, null, true,
- NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, false,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
false, smartActions, null);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
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/OWNERS b/services/core/java/com/android/server/OWNERS
index fe9f1b5..a9c38bc 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -3,3 +3,4 @@
# Vibrator / Threads
per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
+per-file VibratorService.java, DisplayThread.java = ogunwale@google.com
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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b04ae17..b6c4921 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1996,6 +1996,13 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
}
+ if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) {
+ // It can have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE.
+ TelephonyPermissions.checkReadPhoneState(mContext,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
+ Binder.getCallingUid(), callingPackage, "listen to "
+ + "LISTEN_PREFERRED_DATA_SUBID_CHANGE");
+ }
return true;
}
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/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index 98e3299..0d64dbd 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -1,4 +1,5 @@
michaelwr@google.com
+dangittik@google.com
hackbod@google.com
ogunwale@google.com
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/lights/OWNERS b/services/core/java/com/android/server/lights/OWNERS
index 7e7335d..c7c6d56 100644
--- a/services/core/java/com/android/server/lights/OWNERS
+++ b/services/core/java/com/android/server/lights/OWNERS
@@ -1 +1,2 @@
michaelwr@google.com
+dangittik@google.com
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index adfa8d5..ea8c792 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -112,6 +112,7 @@
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.wm.WindowManagerInternal;
import libcore.util.HexEncoding;
@@ -1870,6 +1871,7 @@
DevicePolicyManager dpm = (DevicePolicyManager)
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.reportPasswordChanged(userId);
+ LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
});
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc364c0..3d87634 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -47,17 +47,12 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
-import static android.service.notification.NotificationListenerService
- .HINT_HOST_DISABLE_CALL_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
-import static android.service.notification.NotificationListenerService
- .HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
-import static android.service.notification.NotificationListenerService
- .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
-import static android.service.notification.NotificationListenerService
- .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
-import static android.service.notification.NotificationListenerService
- .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -65,8 +60,7 @@
import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_ERROR;
-import static android.service.notification.NotificationListenerService
- .REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
@@ -583,7 +577,6 @@
private void loadPolicyFile() {
if (DBG) Slog.d(TAG, "loadPolicyFile");
synchronized (mPolicyFile) {
-
InputStream infile = null;
try {
infile = mPolicyFile.openRead();
@@ -3357,14 +3350,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 +3374,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);
}
}
@@ -6700,7 +6689,7 @@
Bundle hidden = new Bundle();
Bundle systemGeneratedSmartActions = new Bundle();
Bundle smartReplies = new Bundle();
- Bundle audiblyAlerted = new Bundle();
+ Bundle lastAudiblyAlerted = new Bundle();
Bundle noisy = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
@@ -6732,7 +6721,7 @@
systemGeneratedSmartActions.putParcelableArrayList(key,
record.getSystemGeneratedSmartActions());
smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
- audiblyAlerted.putBoolean(key, record.getAudiblyAlerted());
+ lastAudiblyAlerted.putLong(key, record.getLastAudiblyAlertedMs());
noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null);
}
final int M = keys.size();
@@ -6745,7 +6734,7 @@
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
- systemGeneratedSmartActions, smartReplies, audiblyAlerted, noisy);
+ systemGeneratedSmartActions, smartReplies, lastAudiblyAlerted, noisy);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 50810cc..39451d4 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -132,6 +132,9 @@
// user
private long mInterruptionTimeMs;
+ // The most recent time the notification made noise or buzzed the device, or -1 if it did not.
+ private long mLastAudiblyAlertedMs;
+
// Is this record an update of an old record?
public boolean isUpdate;
private int mPackagePriority;
@@ -172,7 +175,6 @@
private final NotificationStats mStats;
private int mUserSentiment;
private boolean mIsInterruptive;
- private boolean mAudiblyAlerted;
private boolean mTextChanged;
private boolean mRecordedInterruption;
private int mNumberOfSmartRepliesAdded;
@@ -1051,7 +1053,7 @@
}
public void setAudiblyAlerted(boolean audiblyAlerted) {
- mAudiblyAlerted = audiblyAlerted;
+ mLastAudiblyAlertedMs = audiblyAlerted ? System.currentTimeMillis() : -1;
}
public void setTextChanged(boolean textChanged) {
@@ -1070,9 +1072,9 @@
return mIsInterruptive;
}
- /** Returns true if the notification audibly alerted the user. */
- public boolean getAudiblyAlerted() {
- return mAudiblyAlerted;
+ /** Returns the time the notification audibly alerted the user. */
+ public long getLastAudiblyAlertedMs() {
+ return mLastAudiblyAlertedMs;
}
protected void setPeopleOverride(ArrayList<String> people) {
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..a5a24cf 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;
@@ -1066,9 +1064,6 @@
+ verificationId + " packageName:" + packageName);
return;
}
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "Updating IntentFilterVerificationInfo for package " + packageName
- +" verificationId:" + verificationId);
synchronized (mPackages) {
if (verified) {
@@ -1086,19 +1081,47 @@
int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
boolean needUpdate = false;
- // We cannot override the STATUS_ALWAYS / STATUS_NEVER states if they have
- // already been set by the User thru the Disambiguation dialog
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG,
+ "Updating IntentFilterVerificationInfo for package " + packageName
+ + " verificationId:" + verificationId
+ + " verified=" + verified);
+ }
+
+ // In a success case, we promote from undefined or ASK to ALWAYS. This
+ // supports a flow where the app fails validation but then ships an updated
+ // APK that passes, and therefore deserves to be in ALWAYS.
+ //
+ // If validation failed, the undefined state winds up in the basic ASK behavior,
+ // but apps that previously passed and became ALWAYS are *demoted* out of
+ // that state, since they would not deserve the ALWAYS behavior in case of a
+ // clean install.
switch (userStatus) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+ if (!verified) {
+ // updatedStatus is already UNDEFINED
+ needUpdate = true;
+
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Formerly validated but now failing; demoting");
+ }
+ }
+ break;
+
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ // Stay in 'undefined' on verification failure
if (verified) {
updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
- } else {
- updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
}
needUpdate = true;
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Applying update; old=" + userStatus
+ + " new=" + updatedStatus);
+ }
break;
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ // Keep in 'ask' on failure
if (verified) {
updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
needUpdate = true;
@@ -1114,6 +1137,8 @@
packageName, updatedStatus, userId);
scheduleWritePackageRestrictionsLocked(userId);
}
+ } else {
+ Slog.i(TAG, "autoVerify ignored when installing for all users");
}
}
}
@@ -2168,10 +2193,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 +9237,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
@@ -16713,6 +16735,7 @@
int status = ivi.getStatus();
switch (status) {
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
return true;
@@ -20185,11 +20208,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..b58c811 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/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index 20e4985..244ccb6 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -1,4 +1,5 @@
michaelwr@google.com
+santoscordon@google.com
per-file BatterySaverPolicy.java=omakoto@google.com
per-file ShutdownThread.java=fkupolov@google.com
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 6b0419e..2cab63a 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -42,6 +42,7 @@
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -214,6 +215,27 @@
}
}
}
+ @Override
+ public void onTextClassifierEvent(
+ TextClassificationSessionId sessionId,
+ TextClassifierEvent event) throws RemoteException {
+ Preconditions.checkNotNull(event);
+ final String packageName = event.getEventContext() == null
+ ? null
+ : event.getEventContext().getPackageName();
+ validateInput(mContext, packageName);
+
+ synchronized (mLock) {
+ UserState userState = getCallingUserStateLocked();
+ if (userState.isBoundLocked()) {
+ userState.mService.onTextClassifierEvent(sessionId, event);
+ } else {
+ userState.mPendingRequests.add(new PendingRequest(
+ () -> onTextClassifierEvent(sessionId, event),
+ null /* onServiceFailure */, null /* binder */, this, userState));
+ }
+ }
+ }
@Override
public void onDetectLanguage(
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/utils/UserTokenWatcher.java b/services/core/java/com/android/server/utils/UserTokenWatcher.java
new file mode 100644
index 0000000..a3e58f8
--- /dev/null
+++ b/services/core/java/com/android/server/utils/UserTokenWatcher.java
@@ -0,0 +1,166 @@
+/*
+ * 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.server.utils;
+
+import android.annotation.UserIdInt;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.TokenWatcher;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+
+/**
+ * Multi-user aware {@link TokenWatcher}.
+ *
+ * {@link UserTokenWatcher} is thread-safe.
+ */
+public final class UserTokenWatcher {
+
+ private final Callback mCallback;
+ private final Handler mHandler;
+ private final String mTag;
+
+ @GuardedBy("mWatchers")
+ private final SparseArray<TokenWatcher> mWatchers = new SparseArray<>(1);
+
+ public UserTokenWatcher(Callback callback, Handler handler, String tag) {
+ mCallback = callback;
+ mHandler = handler;
+ mTag = tag;
+ }
+
+ /**
+ * Record that this token has been acquired for the given user. When acquire is called, and
+ * the user's count goes from 0 to 1, the acquired callback is called on the given
+ * handler.
+ *
+ * Note that the same {@code token} can only be acquired once per user. If this
+ * {@code token} has already been acquired for the given user, no action is taken. The first
+ * subsequent call to {@link #release} will release this {@code token}
+ * immediately.
+ *
+ * @param token An IBinder object.
+ * @param tag A string used by the {@link #dump} method for debugging,
+ * to see who has references.
+ * @param userId A user id
+ */
+ public void acquire(IBinder token, String tag, @UserIdInt int userId) {
+ synchronized (mWatchers) {
+ TokenWatcher watcher = mWatchers.get(userId);
+ if (watcher == null) {
+ watcher = new InnerTokenWatcher(userId, mHandler, mTag);
+ mWatchers.put(userId, watcher);
+ }
+ watcher.acquire(token, tag);
+ }
+ }
+
+ /**
+ * Record that this token has been released for the given user. When release is called, and
+ * the user's count goes from 1 to 0, the released callback is called on the given
+ * handler.
+ *
+ * @param token An IBinder object.
+ * @param userId A user id
+ */
+ public void release(IBinder token, @UserIdInt int userId) {
+ synchronized (mWatchers) {
+ TokenWatcher watcher = mWatchers.get(userId);
+ if (watcher != null) {
+ watcher.release(token);
+ }
+ }
+ }
+
+ /**
+ * Returns whether the given user has any registered tokens that have not been cleaned up.
+ *
+ * @return true, if the given user has registered tokens.
+ */
+ public boolean isAcquired(@UserIdInt int userId) {
+ synchronized (mWatchers) {
+ TokenWatcher watcher = mWatchers.get(userId);
+ return watcher != null && watcher.isAcquired();
+ }
+ }
+
+ /**
+ * Dumps the current state.
+ */
+ public void dump(PrintWriter pw) {
+ synchronized (mWatchers) {
+ for (int i = 0; i < mWatchers.size(); i++) {
+ int userId = mWatchers.keyAt(i);
+ TokenWatcher watcher = mWatchers.valueAt(i);
+ if (watcher.isAcquired()) {
+ pw.print("User ");
+ pw.print(userId);
+ pw.println(":");
+ watcher.dump(new IndentingPrintWriter(pw, " "));
+ }
+ }
+ }
+ }
+
+ /**
+ * Callback for {@link UserTokenWatcher}.
+ */
+ public interface Callback {
+
+ /**
+ * Reports that the first token has been acquired for the given user.
+ */
+ void acquired(@UserIdInt int userId);
+
+ /**
+ * Reports that the last token has been release for the given user.
+ */
+ void released(@UserIdInt int userId);
+ }
+
+ private final class InnerTokenWatcher extends TokenWatcher {
+ private final int mUserId;
+
+ private InnerTokenWatcher(int userId, Handler handler, String tag) {
+ super(handler, tag);
+ this.mUserId = userId;
+ }
+
+ @Override
+ public void acquired() {
+ // We MUST NOT hold any locks while invoking the callbacks.
+ mCallback.acquired(mUserId);
+ }
+
+ @Override
+ public void released() {
+ // We MUST NOT hold any locks while invoking the callbacks.
+ mCallback.released(mUserId);
+
+ synchronized (mWatchers) {
+ final TokenWatcher watcher = mWatchers.get(mUserId);
+ if (watcher != null && !watcher.isAcquired()) {
+ mWatchers.remove(mUserId);
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 12690a9..1023182 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..5f00bcc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -86,6 +86,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
@@ -145,6 +146,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;
@@ -582,6 +584,9 @@
if (info.maxAspectRatio != 0) {
pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
}
+ if (info.minAspectRatio != 0) {
+ pw.println(prefix + "minAspectRatio=" + info.minAspectRatio);
+ }
}
}
@@ -2016,10 +2021,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 +2185,7 @@
.getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
if (info != null) {
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- info.windowsFullyDrawnDelayMs);
+ info.windowsFullyDrawnDelayMs, info.getLaunchState());
}
}
@@ -2207,8 +2209,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) {
@@ -2594,7 +2597,10 @@
outBounds.setEmpty();
final float maxAspectRatio = info.maxAspectRatio;
final ActivityStack stack = getActivityStack();
- if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
+ final float minAspectRatio = info.minAspectRatio;
+
+ if (task == null || stack == null || task.inMultiWindowMode()
+ || (maxAspectRatio == 0 && minAspectRatio == 0)
|| isInVrUiMode(getConfiguration())) {
// We don't set override configuration if that activity task isn't fullscreen. I.e. the
// activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
@@ -2609,20 +2615,35 @@
final Rect appBounds = getParent().getWindowConfiguration().getAppBounds();
final int containingAppWidth = appBounds.width();
final int containingAppHeight = appBounds.height();
- int maxActivityWidth = containingAppWidth;
- int maxActivityHeight = containingAppHeight;
+ final float containingRatio = Math.max(containingAppWidth, containingAppHeight)
+ / (float) Math.min(containingAppWidth, containingAppHeight);
- if (containingAppWidth < containingAppHeight) {
- // Width is the shorter side, so we use that to figure-out what the max. height
- // should be given the aspect ratio.
- maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f);
- } else {
- // Height is the shorter side, so we use that to figure-out what the max. width
- // should be given the aspect ratio.
- maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
+ int activityWidth = containingAppWidth;
+ int activityHeight = containingAppHeight;
+
+ if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
+ if (containingAppWidth < containingAppHeight) {
+ // Width is the shorter side, so we use that to figure-out what the max. height
+ // should be given the aspect ratio.
+ activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
+ } else {
+ // Height is the shorter side, so we use that to figure-out what the max. width
+ // should be given the aspect ratio.
+ activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
+ }
+ } else if (containingRatio < minAspectRatio && minAspectRatio != 0) {
+ if (containingAppWidth < containingAppHeight) {
+ // Width is the shorter side, so we use the height to figure-out what the max. width
+ // should be given the aspect ratio.
+ activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
+ } else {
+ // Height is the shorter side, so we use the width to figure-out what the max.
+ // height should be given the aspect ratio.
+ activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
+ }
}
- if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
+ if (containingAppWidth <= activityWidth && containingAppHeight <= activityHeight) {
// The display matches or is less than the activity aspect ratio, so nothing else to do.
// Return the existing bounds. If this method is running for the first time,
// {@link #getRequestedOverrideBounds()} will be empty (representing no override). If
@@ -2637,12 +2658,21 @@
// Also account for the left / top insets (e.g. from display cutouts), which will be clipped
// away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app
// bounds would end up too small.
- outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
+ outBounds.set(0, 0, activityWidth + appBounds.left, activityHeight + appBounds.top);
- if (mAtmService.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
+ final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId());
+ if (navBarPosition == NAV_BAR_LEFT) {
// Position the activity frame on the opposite side of the nav bar.
- outBounds.left = appBounds.right - maxActivityWidth;
+ outBounds.left = appBounds.right - activityWidth;
outBounds.right = appBounds.right;
+ } else if (navBarPosition == NAV_BAR_RIGHT) {
+ // Position the activity frame on the opposite side of the nav bar.
+ outBounds.left = 0;
+ outBounds.right = activityWidth + appBounds.left;
+ } else {
+ // Horizontally center the frame.
+ outBounds.left = appBounds.left + (containingAppWidth - activityWidth) / 2;
+ outBounds.right = outBounds.left + activityWidth;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4339e51..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/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
index 4a20f1a..c9173a6 100644
--- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
+++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
@@ -19,113 +19,142 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.TokenWatcher;
-import android.util.Log;
-import android.util.Pair;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.utils.UserTokenWatcher;
+import com.android.server.wm.LockTaskController.LockTaskToken;
-public class KeyguardDisableHandler extends Handler {
+class KeyguardDisableHandler {
private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardDisableHandler" : TAG_WM;
- private static final int ALLOW_DISABLE_YES = 1;
- private static final int ALLOW_DISABLE_NO = 0;
- private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager
- private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher
+ private final UserTokenWatcher mAppTokenWatcher;
+ private final UserTokenWatcher mSystemTokenWatcher;
- // Message.what constants
- static final int KEYGUARD_DISABLE = 1;
- static final int KEYGUARD_REENABLE = 2;
- static final int KEYGUARD_POLICY_CHANGED = 3;
+ private int mCurrentUser = UserHandle.USER_SYSTEM;
+ private Injector mInjector;
- final Context mContext;
- final WindowManagerPolicy mPolicy;
- KeyguardTokenWatcher mKeyguardTokenWatcher;
-
- public KeyguardDisableHandler(final Context context, final WindowManagerPolicy policy) {
- mContext = context;
- mPolicy = policy;
+ @VisibleForTesting
+ KeyguardDisableHandler(Injector injector, Handler handler) {
+ mInjector = injector;
+ mAppTokenWatcher = new UserTokenWatcher(mCallback, handler, TAG);
+ mSystemTokenWatcher = new UserTokenWatcher(mCallback, handler, TAG);
}
- @SuppressWarnings("unchecked")
- @Override
- public void handleMessage(Message msg) {
- if (mKeyguardTokenWatcher == null) {
- mKeyguardTokenWatcher = new KeyguardTokenWatcher(this);
- }
-
- switch (msg.what) {
- case KEYGUARD_DISABLE:
- final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj;
- mKeyguardTokenWatcher.acquire(pair.first, pair.second);
- break;
-
- case KEYGUARD_REENABLE:
- mKeyguardTokenWatcher.release((IBinder)msg.obj);
- break;
-
- case KEYGUARD_POLICY_CHANGED:
- mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN;
- if (mKeyguardTokenWatcher.isAcquired()) {
- // If we are currently disabled we need to know if the keyguard
- // should be re-enabled, so determine the allow state immediately.
- mKeyguardTokenWatcher.updateAllowState();
- if (mAllowDisableKeyguard != ALLOW_DISABLE_YES) {
- mPolicy.enableKeyguard(true);
- }
- } else {
- // lazily evaluate this next time we're asked to disable keyguard
- mPolicy.enableKeyguard(true);
- }
- break;
+ public void setCurrentUser(int user) {
+ synchronized (this) {
+ mCurrentUser = user;
+ updateKeyguardEnabledLocked(UserHandle.USER_ALL);
}
}
- class KeyguardTokenWatcher extends TokenWatcher {
-
- public KeyguardTokenWatcher(final Handler handler) {
- super(handler, TAG);
+ void updateKeyguardEnabled(int userId) {
+ synchronized (this) {
+ updateKeyguardEnabledLocked(userId);
}
+ }
- public void updateAllowState() {
- // We fail safe and prevent disabling keyguard in the unlikely event this gets
- // called before DevicePolicyManagerService has started.
- DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- try {
- mAllowDisableKeyguard = dpm.getPasswordQuality(null,
- ActivityManager.getService().getCurrentUser().id)
- == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ?
- ALLOW_DISABLE_YES : ALLOW_DISABLE_NO;
- } catch (RemoteException re) {
- // Nothing much we can do
- }
- }
+ private void updateKeyguardEnabledLocked(int userId) {
+ if (mCurrentUser == userId || userId == UserHandle.USER_ALL) {
+ mInjector.enableKeyguard(shouldKeyguardBeEnabled(mCurrentUser));
+ }
+ }
+
+ void disableKeyguard(IBinder token, String tag, int callingUid, int userId) {
+ UserTokenWatcher watcherForCaller = watcherForCallingUid(token, callingUid);
+ watcherForCaller.acquire(token, tag, mInjector.getProfileParentId(userId));
+ }
+
+ void reenableKeyguard(IBinder token, int callingUid, int userId) {
+ UserTokenWatcher watcherForCaller = watcherForCallingUid(token, callingUid);
+ watcherForCaller.release(token, mInjector.getProfileParentId(userId));
+ }
+
+ private UserTokenWatcher watcherForCallingUid(IBinder token, int callingUid) {
+ if (Process.isApplicationUid(callingUid)) {
+ return mAppTokenWatcher;
+ } else if (callingUid == Process.SYSTEM_UID && token instanceof LockTaskToken) {
+ // We allow the lock task token here as a legacy case, because it enforces its own
+ // security guarantees.
+ // NOTE: DO NOT add new usages of this API in system server. It is deprecated and
+ // easily misused.
+ return mSystemTokenWatcher;
+ } else {
+ throw new UnsupportedOperationException("Only apps can use the KeyguardLock API");
+ }
+ }
+
+ private boolean shouldKeyguardBeEnabled(int userId) {
+ final boolean dpmRequiresPassword = mInjector.dpmRequiresPassword(mCurrentUser);
+ final boolean keyguardSecure = mInjector.isKeyguardSecure(mCurrentUser);
+
+ final boolean allowedFromApps = !dpmRequiresPassword && !keyguardSecure;
+ // The system can disable the keyguard for lock task mode even if the keyguard is secure,
+ // because it enforces its own security guarantees.
+ final boolean allowedFromSystem = !dpmRequiresPassword;
+
+ final boolean shouldBeDisabled = allowedFromApps && mAppTokenWatcher.isAcquired(userId)
+ || allowedFromSystem && mSystemTokenWatcher.isAcquired(userId);
+ return !shouldBeDisabled;
+ }
+
+ // Callback happens on mHandler thread.
+ private final UserTokenWatcher.Callback mCallback = new UserTokenWatcher.Callback() {
+ @Override
+ public void acquired(int userId) {
+ updateKeyguardEnabled(userId);
}
@Override
- public void acquired() {
- if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) {
- updateAllowState();
- }
- if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) {
- mPolicy.enableKeyguard(false);
- } else {
- Log.v(TAG, "Not disabling keyguard since device policy is enforced");
- }
+ public void released(int userId) {
+ updateKeyguardEnabled(userId);
}
+ };
- @Override
- public void released() {
- mPolicy.enableKeyguard(true);
- }
+ static KeyguardDisableHandler create(Context context, WindowManagerPolicy policy,
+ Handler handler) {
+ final UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+ return new KeyguardDisableHandler(new Injector() {
+ @Override
+ public boolean dpmRequiresPassword(int userId) {
+ DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ return dpm == null || dpm.getPasswordQuality(null, userId)
+ != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ }
+
+ @Override
+ public boolean isKeyguardSecure(int userId) {
+ return policy.isKeyguardSecure(userId);
+ }
+
+ @Override
+ public int getProfileParentId(int userId) {
+ return userManager.getProfileParentId(userId);
+ }
+
+ @Override
+ public void enableKeyguard(boolean enabled) {
+ policy.enableKeyguard(enabled);
+ }
+ }, handler);
+ }
+
+ interface Injector {
+ boolean dpmRequiresPassword(int userId);
+
+ boolean isKeyguardSecure(int userId);
+
+ int getProfileParentId(int userId);
+
+ void enableKeyguard(boolean enabled);
}
}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 3b66f7d..e6e6275 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -54,6 +54,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.util.Pair;
@@ -126,7 +127,7 @@
/** Tag used for disabling of keyguard */
private static final String LOCK_TASK_TAG = "Lock-to-App";
- private final IBinder mToken = new Binder();
+ private final IBinder mToken = new LockTaskToken();
private final ActivityStackSupervisor mSupervisor;
private final Context mContext;
@@ -180,6 +181,17 @@
*/
private final Handler mHandler;
+ /**
+ * Stores the user for which we're trying to dismiss the keyguard and then subsequently
+ * disable it.
+ *
+ * Tracking this ensures we don't mistakenly disable the keyguard if we've stopped trying to
+ * between the dismiss request and when it succeeds.
+ *
+ * Must only be accessed from the Handler thread.
+ */
+ private int mPendingDisableFromDismiss = UserHandle.USER_NULL;
+
LockTaskController(Context context, ActivityStackSupervisor supervisor,
Handler handler) {
mContext = context;
@@ -740,16 +752,18 @@
* Should only be called on the handler thread to avoid race.
*/
private void setKeyguardState(int lockTaskModeState, int userId) {
+ mPendingDisableFromDismiss = UserHandle.USER_NULL;
if (lockTaskModeState == LOCK_TASK_MODE_NONE) {
- mWindowManager.reenableKeyguard(mToken);
+ mWindowManager.reenableKeyguard(mToken, userId);
} else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
if (isKeyguardAllowed(userId)) {
- mWindowManager.reenableKeyguard(mToken);
+ mWindowManager.reenableKeyguard(mToken, userId);
} else {
// If keyguard is not secure and it is locked, dismiss the keyguard before
// disabling it, which avoids the platform to think the keyguard is still on.
if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) {
+ mPendingDisableFromDismiss = userId;
mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() {
@Override
public void onDismissError() throws RemoteException {
@@ -759,7 +773,13 @@
@Override
public void onDismissSucceeded() throws RemoteException {
mHandler.post(
- () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG));
+ () -> {
+ if (mPendingDisableFromDismiss == userId) {
+ mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG,
+ userId);
+ mPendingDisableFromDismiss = UserHandle.USER_NULL;
+ }
+ });
}
@Override
@@ -768,12 +788,12 @@
}
}, null);
} else {
- mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+ mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, userId);
}
}
} else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
- mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+ mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, userId);
}
}
@@ -898,4 +918,10 @@
default: return "unknown=" + mLockTaskModeState;
}
}
+
+ /** Marker class for the token used to disable keyguard. */
+ static class LockTaskToken extends Binder {
+ private LockTaskToken() {
+ }
+ }
}
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/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9f1a587..646fdd9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -334,6 +334,11 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
+ * Reports that the password for the given user has changed.
+ */
+ public abstract void reportPasswordChanged(int userId);
+
+ /**
* Retrieves a height of input method window for given display.
*/
public abstract int getInputMethodWindowVisibleHeight(int displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 002d6d4..d0521b4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
@@ -72,7 +73,6 @@
import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -181,7 +181,6 @@
import android.util.EventLog;
import android.util.Log;
import android.util.MergedConfiguration;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -235,6 +234,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
@@ -396,7 +396,7 @@
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED:
- mKeyguardDisableHandler.sendEmptyMessage(KEYGUARD_POLICY_CHANGED);
+ mKeyguardDisableHandler.updateKeyguardEnabled(getSendingUserId());
break;
}
}
@@ -961,7 +961,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
+ mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -1040,7 +1040,7 @@
IntentFilter filter = new IntentFilter();
// Track changes to DevicePolicyManager state so we can enable/disable keyguard.
filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
mLatencyTracker = LatencyTracker.getInstance(context);
@@ -2817,45 +2817,38 @@
}
@Override
- public void disableKeyguard(IBinder token, String tag) {
+ public void disableKeyguard(IBinder token, String tag, int userId) {
+ userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false /* allowAll */, ALLOW_FULL_ONLY, "disableKeyguard", null);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- // If this isn't coming from the system then don't allow disabling the lockscreen
- // to bypass security.
- if (Binder.getCallingUid() != SYSTEM_UID && isKeyguardSecure()) {
- Log.d(TAG_WM, "current mode is SecurityMode, ignore disableKeyguard");
- return;
+ final int callingUid = Binder.getCallingUid();
+ final long origIdentity = Binder.clearCallingIdentity();
+ try {
+ mKeyguardDisableHandler.disableKeyguard(token, tag, callingUid, userId);
+ } finally {
+ Binder.restoreCallingIdentity(origIdentity);
}
-
- // If this isn't coming from the current profiles, ignore it.
- if (!isCurrentProfileLocked(UserHandle.getCallingUserId())) {
- Log.d(TAG_WM, "non-current profiles, ignore disableKeyguard");
- return;
- }
-
- if (token == null) {
- throw new IllegalArgumentException("token == null");
- }
-
- mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
- KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
}
@Override
- public void reenableKeyguard(IBinder token) {
+ public void reenableKeyguard(IBinder token, int userId) {
+ userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false /* allowAll */, ALLOW_FULL_ONLY, "reenableKeyguard", null);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
-
- if (token == null) {
- throw new IllegalArgumentException("token == null");
+ Preconditions.checkNotNull(token, "token is null");
+ final int callingUid = Binder.getCallingUid();
+ final long origIdentity = Binder.clearCallingIdentity();
+ try {
+ mKeyguardDisableHandler.reenableKeyguard(token, callingUid, userId);
+ } finally {
+ Binder.restoreCallingIdentity(origIdentity);
}
-
- mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
- KeyguardDisableHandler.KEYGUARD_REENABLE, token));
}
/**
@@ -3139,11 +3132,7 @@
mCurrentUserId = newUserId;
mCurrentProfileIds = currentProfileIds;
mPolicy.setCurrentUserLw(newUserId);
-
- // If keyguard was disabled, re-enable it
- // TODO: Keep track of keyguardEnabled state per user and use here...
- // e.g. enabled = mKeyguardDisableHandler.getEnabledStateForUser(newUserId);
- mPolicy.enableKeyguard(true);
+ mKeyguardDisableHandler.setCurrentUser(newUserId);
// Hide windows that should not be seen by the new user.
mRoot.switchUser();
@@ -7124,6 +7113,11 @@
}
@Override
+ public void reportPasswordChanged(int userId) {
+ mKeyguardDisableHandler.updateKeyguardEnabled(userId);
+ }
+
+ @Override
public int getInputMethodWindowVisibleHeight(int displayId) {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7186cdf..fc5ed2a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -478,7 +478,8 @@
/**
* Strings logged with {@link
- * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}.
+ * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}
+ * and {@link DevicePolicyEnums#PROVISIONING_ENTRY_POINT_ADB}.
*/
private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
@@ -4328,6 +4329,11 @@
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_WIDGET_PROVIDER)
+ .setAdmin(admin)
+ .write();
+
if (changedProviders != null) {
mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
return true;
@@ -4355,6 +4361,11 @@
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REMOVE_CROSS_PROFILE_WIDGET_PROVIDER)
+ .setAdmin(admin)
+ .write();
+
if (changedProviders != null) {
mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders);
return true;
@@ -5491,6 +5502,12 @@
final long id = mInjector.binderClearCallingIdentity();
try {
mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases);
+ final boolean isDelegate = (admin == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.UNINSTALL_CA_CERTS)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .write();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -5556,7 +5573,14 @@
final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
try {
IKeyChainService keyChain = keyChainConnection.getService();
- return keyChain.removeKeyPair(alias);
+ final boolean result = keyChain.removeKeyPair(alias);
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REMOVE_KEY_PAIR)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .write();
+ return result;
} catch (RemoteException e) {
Log.e(LOG_TAG, "Removing keypair", e);
} finally {
@@ -5740,6 +5764,14 @@
return false;
}
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setInt(idAttestationFlags)
+ .setStrings(algorithm)
+ .write();
return true;
}
} catch (RemoteException e) {
@@ -5768,6 +5800,12 @@
return false;
}
keyChain.setUserSelectable(alias, isUserSelectable);
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_KEY_PAIR_CERTIFICATE)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .write();
return true;
} catch (InterruptedException e) {
Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
@@ -5830,6 +5868,12 @@
sendPrivateKeyAliasResponse(chosenAlias, response);
}
}, null, Activity.RESULT_OK, null, null);
+ final String adminPackageName =
+ (aliasChooser != null ? aliasChooser.getPackageName() : null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.CHOOSE_PRIVATE_KEY_ALIAS)
+ .setAdmin(adminPackageName)
+ .write();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -6219,6 +6263,11 @@
public void setCertInstallerPackage(ComponentName who, String installerPackage)
throws SecurityException {
setDelegatedScopePreO(who, installerPackage, DELEGATION_CERT_INSTALL);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_CERT_INSTALLER_PACKAGE)
+ .setAdmin(who)
+ .setStrings(installerPackage)
+ .write();
}
@Override
@@ -6250,6 +6299,13 @@
if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdown)) {
throw new UnsupportedOperationException();
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_ALWAYS_ON_VPN_PACKAGE)
+ .setAdmin(admin)
+ .setStrings(vpnPackage)
+ .setBoolean(lockdown)
+ .setInt(/* number of vpn packages */ 0)
+ .write();
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
@@ -6965,6 +7021,11 @@
updateScreenCaptureDisabled(userHandle, disabled);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SCREEN_CAPTURE_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
}
/**
@@ -7036,6 +7097,11 @@
mInjector.binderRestoreCallingIdentity(ident);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_AUTO_TIME_REQUIRED)
+ .setAdmin(who)
+ .setBoolean(required)
+ .write();
}
/**
@@ -7167,6 +7233,10 @@
DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REQUEST_BUGREPORT)
+ .setAdmin(who)
+ .write();
return true;
} catch (RemoteException re) {
// should never happen
@@ -7377,6 +7447,11 @@
}
// Tell the user manager that the restrictions have changed.
pushUserRestrictions(userHandle);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
}
/**
@@ -7528,6 +7603,13 @@
// Notify package manager.
mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList);
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_KEEP_UNINSTALLED_PACKAGES)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageList.toArray(new String[0]))
+ .write();
}
@Override
@@ -7585,6 +7667,11 @@
if (isAdb()) {
// Log device owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_ADB)
+ .setAdmin(admin)
+ .setStrings(LOG_TAG_DEVICE_OWNER)
+ .write();
}
mOwners.setDeviceOwner(admin, ownerName, userId);
@@ -7854,6 +7941,11 @@
if (isAdb()) {
// Log profile owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_ADB)
+ .setAdmin(who)
+ .setStrings(LOG_TAG_PROFILE_OWNER)
+ .write();
}
mOwners.setProfileOwner(who, ownerName, userHandle);
@@ -7941,6 +8033,10 @@
mInjector.binderRestoreCallingIdentity(token);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_DEVICE_OWNER_LOCK_SCREEN_INFO)
+ .setAdmin(who)
+ .write();
}
@Override
@@ -8127,6 +8223,10 @@
final long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setUserName(userId, profileName);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PROFILE_NAME)
+ .setAdmin(who)
+ .write();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -8601,6 +8701,13 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ final String activityPackage =
+ (activity != null ? activity.getPackageName() : null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.ADD_PERSISTENT_PREFERRED_ACTIVITY)
+ .setAdmin(who)
+ .setStrings(activityPackage, getIntentFilterActions(filter))
+ .write();
}
@Override
@@ -8664,6 +8771,13 @@
final long id = mInjector.binderClearCallingIdentity();
try {
mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_APPLICATION_RESTRICTIONS)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageName)
+ .write();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -8795,6 +8909,24 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_INTENT_FILTER)
+ .setAdmin(who)
+ .setStrings(getIntentFilterActions(filter))
+ .setInt(flags)
+ .write();
+ }
+
+ private static String[] getIntentFilterActions(IntentFilter filter) {
+ if (filter == null) {
+ return null;
+ }
+ final int actionsCount = filter.countActions();
+ final String[] actions = new String[actionsCount];
+ for (int i = 0; i < actionsCount; i++) {
+ actions[i] = filter.getAction(i);
+ }
+ return actions;
}
@Override
@@ -8914,6 +9046,13 @@
admin.permittedAccessiblityServices = packageList;
saveSettingsLocked(UserHandle.getCallingUserId());
}
+ final String[] packageArray =
+ packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PERMITTED_ACCESSIBILITY_SERVICES)
+ .setAdmin(who)
+ .setStrings(packageArray)
+ .write();
return true;
}
@@ -9088,6 +9227,13 @@
admin.permittedInputMethods = packageList;
saveSettingsLocked(callingUserId);
}
+ final String[] packageArray =
+ packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PERMITTED_INPUT_METHODS)
+ .setAdmin(who)
+ .setStrings(packageArray)
+ .write();
return true;
}
@@ -9628,6 +9774,7 @@
public String[] setPackagesSuspended(ComponentName who, String callerPackage,
String[] packageNames, boolean suspended) {
int callingUserId = UserHandle.getCallingUserId();
+ String[] result = null;
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or a package access delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9635,7 +9782,8 @@
long id = mInjector.binderClearCallingIdentity();
try {
- return mIPackageManager.setPackagesSuspendedAsUser(packageNames, suspended,
+ result = mIPackageManager
+ .setPackagesSuspendedAsUser(packageNames, suspended,
null, null, null, PLATFORM_PACKAGE_NAME, callingUserId);
} catch (RemoteException re) {
// Shouldn't happen.
@@ -9643,8 +9791,18 @@
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
- return packageNames;
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PACKAGES_SUSPENDED)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageNames)
+ .write();
+ if (result != null) {
+ return result;
+ }
+ return packageNames;
}
@Override
@@ -9781,6 +9939,7 @@
public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
boolean hidden) {
int callingUserId = UserHandle.getCallingUserId();
+ boolean result = false;
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or a package access delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9788,16 +9947,23 @@
long id = mInjector.binderClearCallingIdentity();
try {
- return mIPackageManager.setApplicationHiddenSettingAsUser(
- packageName, hidden, callingUserId);
+ result = mIPackageManager
+ .setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
} catch (RemoteException re) {
// shouldn't happen
Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
- return false;
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageName, hidden ? "hidden" : "not_hidden")
+ .write();
+ return result;
}
@Override
@@ -9862,10 +10028,18 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.ENABLE_SYSTEM_APP)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageName)
+ .write();
}
@Override
public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
+ int numberOfAppsInstalled = 0;
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or an enable system app delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9887,7 +10061,6 @@
if (VERBOSE_LOG) {
Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
}
- int numberOfAppsInstalled = 0;
if (activitiesToEnable != null) {
for (ResolveInfo info : activitiesToEnable) {
if (info.activityInfo != null) {
@@ -9903,7 +10076,6 @@
}
}
}
- return numberOfAppsInstalled;
} catch (RemoteException e) {
// shouldn't happen
Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
@@ -9912,6 +10084,14 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.ENABLE_SYSTEM_APP_WITH_INTENT)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(intent.getAction())
+ .write();
+ return numberOfAppsInstalled;
}
private boolean isSystemApp(IPackageManager pm, String packageName, int userId)
@@ -9928,6 +10108,7 @@
@Override
public boolean installExistingPackage(ComponentName who, String callerPackage,
String packageName) {
+ boolean result;
synchronized (getLockObject()) {
// Ensure the caller is a PO or an install existing package delegate
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -9946,7 +10127,8 @@
}
// Install the package.
- return mIPackageManager.installExistingPackageAsUser(packageName, callingUserId,
+ result = mIPackageManager
+ .installExistingPackageAsUser(packageName, callingUserId,
0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY)
== PackageManager.INSTALL_SUCCEEDED;
} catch (RemoteException re) {
@@ -9956,6 +10138,16 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ if (result) {
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_EXISTING_PACKAGE)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageName)
+ .write();
+ }
+ return result;
}
@Override
@@ -10019,6 +10211,13 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ final boolean isDelegate = (who == null);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_UNINSTALL_BLOCKED)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
+ .setStrings(packageName)
+ .write();
}
@Override
@@ -10060,6 +10259,11 @@
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CALLER_ID_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
}
@Override
@@ -10098,6 +10302,11 @@
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
}
@Override
@@ -10197,6 +10406,11 @@
saveSettingsLocked(UserHandle.getCallingUserId());
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_BLUETOOTH_CONTACT_SHARING_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
}
@Override
@@ -10356,6 +10570,12 @@
} else {
sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_EXITING);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_LOCKTASK_MODE_ENABLED)
+ .setAdmin(admin.info.getPackageName())
+ .setBoolean(isEnabled)
+ .setStrings(pkg)
+ .write();
}
}
}
@@ -10524,6 +10744,11 @@
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
+ .setAdmin(who)
+ .setBoolean(on)
+ .write();
}
}
@@ -10553,6 +10778,10 @@
mInjector.binderRestoreCallingIdentity(id);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_USER_ICON)
+ .setAdmin(who)
+ .write();
}
@Override
@@ -10578,6 +10807,11 @@
}
mLockPatternUtils.setLockScreenDisabled(disabled, userId);
mInjector.getIWindowManager().dismissKeyguard(null /* callback */, null /* message */);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_KEYGUARD_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
} catch (RemoteException e) {
// Same process, does not happen.
} finally {
@@ -10616,6 +10850,11 @@
saveSettingsLocked(userId);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_STATUS_BAR_DISABLED)
+ .setAdmin(who)
+ .setBoolean(disabled)
+ .write();
return true;
}
@@ -11036,6 +11275,11 @@
mContext.sendBroadcastAsUser(
new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
UserHandle.SYSTEM);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SYSTEM_UPDATE_POLICY)
+ .setAdmin(who)
+ .setInt(policy != null ? policy.getPolicyType() : 0)
+ .write();
}
@Override
@@ -11588,7 +11832,12 @@
if (wifiInfo == null) {
return null;
}
- return wifiInfo.hasRealMacAddress() ? wifiInfo.getMacAddress() : null;
+ final String result = wifiInfo.hasRealMacAddress() ? wifiInfo.getMacAddress() : null;
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.GET_WIFI_MAC_ADDRESS)
+ .setAdmin(admin)
+ .write();
+ return result;
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -11634,6 +11883,10 @@
if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
throw new IllegalStateException("Cannot be called with ongoing call on the device");
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REBOOT)
+ .setAdmin(admin)
+ .write();
mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
@@ -11655,6 +11908,10 @@
saveSettingsLocked(userHandle);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SHORT_SUPPORT_MESSAGE)
+ .setAdmin(who)
+ .write();
}
@Override
@@ -11685,6 +11942,10 @@
saveSettingsLocked(userHandle);
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_LONG_SUPPORT_MESSAGE)
+ .setAdmin(who)
+ .write();
}
@Override
@@ -11750,6 +12011,10 @@
admin.organizationColor = color;
saveSettingsLocked(userHandle);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_ORGANIZATION_COLOR)
+ .setAdmin(who)
+ .write();
}
@Override
@@ -13153,6 +13418,7 @@
}
final long id = mInjector.binderClearCallingIdentity();
+ String ownerType = null;
try {
synchronized (getLockObject()) {
/*
@@ -13173,6 +13439,7 @@
bundle = new PersistableBundle();
}
if (isProfileOwner(admin, callingUserId)) {
+ ownerType = ADMIN_TYPE_PROFILE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_PROFILE_OWNER);
transferProfileOwnershipLocked(admin, target, callingUserId);
@@ -13183,6 +13450,7 @@
notifyAffiliatedProfileTransferOwnershipComplete(callingUserId);
}
} else if (isDeviceOwner(admin, callingUserId)) {
+ ownerType = ADMIN_TYPE_DEVICE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_DEVICE_OWNER);
transferDeviceOwnershipLocked(admin, target, callingUserId);
@@ -13194,6 +13462,11 @@
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.TRANSFER_OWNERSHIP)
+ .setAdmin(admin)
+ .setStrings(target.getPackageName(), ownerType)
+ .write();
}
private void prepareTransfer(ComponentName admin, ComponentName target,
@@ -13669,6 +13942,11 @@
mContext, updateFileDescriptor, callback, mInjector, mConstants);
}
updateInstaller.startInstallUpdate();
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.INSTALL_SYSTEM_UPDATE)
+ .setAdmin(admin)
+ .setBoolean(isDeviceAB())
+ .write();
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -13694,6 +13972,11 @@
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.ADD_CROSS_PROFILE_CALENDAR_PACKAGE)
+ .setAdmin(who)
+ .setStrings(packageName)
+ .write();
}
@Override
@@ -13713,6 +13996,13 @@
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
+ if (isRemoved) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE)
+ .setAdmin(who)
+ .setStrings(packageName)
+ .write();
+ }
return isRemoved;
}
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..f4cdc8c 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/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 41d5a1c..afbe6bc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -27,6 +27,8 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -417,7 +419,7 @@
verifyLights();
assertTrue(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -430,7 +432,7 @@
verifyNeverVibrate();
verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -441,7 +443,7 @@
verifyBeep();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -452,7 +454,7 @@
verifyNeverBeep();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -490,7 +492,7 @@
verifyNeverBeep();
verifyNeverVibrate();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -503,7 +505,7 @@
verifyNeverBeep();
verifyNeverVibrate();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -520,7 +522,7 @@
verifyBeepLooped();
verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -549,7 +551,7 @@
verifyNeverStopAudio();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -563,9 +565,9 @@
verifyNeverStopAudio();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
/**
@@ -602,7 +604,7 @@
mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
verifyNeverStopAudio();
assertTrue(other.isInterruptive());
- assertTrue(other.getAudiblyAlerted());
+ assertNotEquals(-1, other.getLastAudiblyAlertedMs());
}
@Test
@@ -628,14 +630,14 @@
// set up internal state
mService.buzzBeepBlinkLocked(r);
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
Mockito.reset(mRingtonePlayer);
// quiet update should stop making noise
mService.buzzBeepBlinkLocked(s);
verifyStopAudio();
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -647,14 +649,14 @@
// set up internal state
mService.buzzBeepBlinkLocked(r);
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
Mockito.reset(mRingtonePlayer);
// stop making noise - this is a weird corner case, but quiet should override once
mService.buzzBeepBlinkLocked(s);
verifyStopAudio();
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -671,7 +673,7 @@
verify(mService, times(1)).playInCallNotification();
verifyNeverBeep(); // doesn't play normal beep
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -691,7 +693,7 @@
eq(effect), anyString(),
(AudioAttributes) anyObject());
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -709,7 +711,7 @@
verifyNeverVibrate();
verifyBeepLooped();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -729,7 +731,7 @@
verify(mRingtonePlayer, never()).playAsync
(anyObject(), anyObject(), anyBoolean(), anyObject());
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -746,7 +748,7 @@
verifyDelayedVibrateLooped();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -758,7 +760,7 @@
verifyNeverBeep();
verifyVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -768,7 +770,7 @@
mService.buzzBeepBlinkLocked(r);
verifyVibrateLooped();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -784,7 +786,7 @@
mService.buzzBeepBlinkLocked(r);
verifyVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -795,7 +797,7 @@
verifyNeverBeep();
assertFalse(child.isInterruptive());
- assertFalse(child.getAudiblyAlerted());
+ assertEquals(-1, child.getLastAudiblyAlertedMs());
}
@Test
@@ -808,7 +810,7 @@
verifyBeepLooped();
// summaries are never interruptive for notification counts
assertFalse(summary.isInterruptive());
- assertTrue(summary.getAudiblyAlerted());
+ assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
}
@Test
@@ -819,7 +821,7 @@
verifyBeepLooped();
assertTrue(nonGroup.isInterruptive());
- assertTrue(nonGroup.getAudiblyAlerted());
+ assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@Test
@@ -831,7 +833,7 @@
verifyNeverBeep();
assertFalse(summary.isInterruptive());
- assertFalse(summary.getAudiblyAlerted());
+ assertEquals(-1, summary.getLastAudiblyAlertedMs());
}
@Test
@@ -842,7 +844,7 @@
verifyBeepLooped();
assertTrue(child.isInterruptive());
- assertTrue(child.getAudiblyAlerted());
+ assertNotEquals(-1, child.getLastAudiblyAlertedMs());
}
@Test
@@ -853,7 +855,7 @@
verifyBeepLooped();
assertTrue(nonGroup.isInterruptive());
- assertTrue(nonGroup.getAudiblyAlerted());
+ assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@Test
@@ -864,7 +866,7 @@
verifyBeepLooped();
assertTrue(group.isInterruptive());
- assertTrue(group.getAudiblyAlerted());
+ assertNotEquals(-1, group.getLastAudiblyAlertedMs());
}
@Test
@@ -877,13 +879,13 @@
mService.buzzBeepBlinkLocked(r);
Mockito.reset(mVibrator);
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
// update should not beep
mService.buzzBeepBlinkLocked(s);
verifyNeverVibrate();
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -896,7 +898,7 @@
verifyNeverStopVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -910,9 +912,9 @@
verifyNeverStopVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -931,11 +933,11 @@
mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
verifyNeverStopVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
assertTrue(other.isInterruptive());
- assertTrue(other.getAudiblyAlerted());
+ assertNotEquals(-1, other.getLastAudiblyAlertedMs());
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -951,7 +953,7 @@
mService.buzzBeepBlinkLocked(other);
verifyNeverStopVibrate();
assertFalse(other.isInterruptive());
- assertFalse(other.getAudiblyAlerted());
+ assertEquals(-1, other.getLastAudiblyAlertedMs());
}
@Test
@@ -968,9 +970,9 @@
mService.buzzBeepBlinkLocked(s);
verifyStopVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -987,9 +989,9 @@
mService.buzzBeepBlinkLocked(s);
verifyStopVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -1007,9 +1009,9 @@
mService.buzzBeepBlinkLocked(s);
verifyStopVibrate();
assertTrue(r.isInterruptive());
- assertTrue(r.getAudiblyAlerted());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
assertFalse(s.isInterruptive());
- assertFalse(s.getAudiblyAlerted());
+ assertEquals(-1, s.getLastAudiblyAlertedMs());
}
@Test
@@ -1027,7 +1029,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverBeep();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1039,7 +1041,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverBeep();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1082,7 +1084,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverBeep();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1116,7 +1118,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1126,7 +1128,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1135,7 +1137,7 @@
mService.buzzBeepBlinkLocked(r);
verifyLights();
assertTrue(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
r = getLightsOnceNotification();
r.isUpdate = true;
@@ -1143,7 +1145,7 @@
// checks that lights happened once, i.e. this new call didn't trigger them again
verifyLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1153,7 +1155,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1162,7 +1164,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1172,7 +1174,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1182,7 +1184,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1192,7 +1194,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
assertFalse(r.isInterruptive());
- assertFalse(r.getAudiblyAlerted());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
@@ -1203,7 +1205,7 @@
verifyNeverLights();
assertFalse(child.isInterruptive());
- assertFalse(child.getAudiblyAlerted());
+ assertEquals(-1, child.getLastAudiblyAlertedMs());
}
@Test
@@ -1216,7 +1218,7 @@
verifyLights();
// summaries should never count for interruptiveness counts
assertFalse(summary.isInterruptive());
- assertFalse(summary.getAudiblyAlerted());
+ assertEquals(-1, summary.getLastAudiblyAlertedMs());
}
@Test
@@ -1227,7 +1229,7 @@
verifyLights();
assertTrue(nonGroup.isInterruptive());
- assertFalse(nonGroup.getAudiblyAlerted());
+ assertEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@Test
@@ -1239,7 +1241,7 @@
verifyNeverLights();
assertFalse(summary.isInterruptive());
- assertFalse(summary.getAudiblyAlerted());
+ assertEquals(-1, summary.getLastAudiblyAlertedMs());
}
@Test
@@ -1250,7 +1252,7 @@
verifyLights();
assertTrue(child.isInterruptive());
- assertFalse(child.getAudiblyAlerted());
+ assertEquals(-1, child.getLastAudiblyAlertedMs());
}
@Test
@@ -1261,7 +1263,7 @@
verifyLights();
assertTrue(nonGroup.isInterruptive());
- assertFalse(nonGroup.getAudiblyAlerted());
+ assertEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@Test
@@ -1272,7 +1274,7 @@
verifyLights();
assertTrue(group.isInterruptive());
- assertFalse(group.getAudiblyAlerted());
+ assertEquals(-1, group.getLastAudiblyAlertedMs());
}
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index bcba15d..daca9cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -16,12 +16,9 @@
package com.android.server.notification;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -92,7 +89,7 @@
assertEquals(getShowBadge(i), ranking.canShowBadge());
assertEquals(getUserSentiment(i), ranking.getUserSentiment());
assertEquals(getHidden(i), ranking.isSuspended());
- assertEquals(audiblyAlerted(i), ranking.audiblyAlerted());
+ assertEquals(lastAudiblyAlerted(i), ranking.getLastAudiblyAlertedMillis());
assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
}
@@ -113,7 +110,7 @@
Bundle mHidden = new Bundle();
Bundle smartActions = new Bundle();
Bundle smartReplies = new Bundle();
- Bundle audiblyAlerted = new Bundle();
+ Bundle lastAudiblyAlerted = new Bundle();
Bundle noisy = new Bundle();
for (int i = 0; i < mKeys.length; i++) {
@@ -134,14 +131,14 @@
mHidden.putBoolean(key, getHidden(i));
smartActions.putParcelableArrayList(key, getSmartActions(key, i));
smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
- audiblyAlerted.putBoolean(key, audiblyAlerted(i));
+ lastAudiblyAlerted.putLong(key, lastAudiblyAlerted(i));
noisy.putBoolean(key, getNoisy(i));
}
NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
interceptedKeys.toArray(new String[0]), visibilityOverrides,
suppressedVisualEffects, importance, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
- smartActions, smartReplies, audiblyAlerted, noisy);
+ smartActions, smartReplies, lastAudiblyAlerted, noisy);
return update;
}
@@ -193,8 +190,8 @@
return index % 2 == 0;
}
- private boolean audiblyAlerted(int index) {
- return index < 2;
+ private long lastAudiblyAlerted(int index) {
+ return index * 2000;
}
private boolean getNoisy(int index) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 0eeeeed..65e640f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -19,12 +19,9 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_POSITIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -806,6 +803,18 @@
}
@Test
+ public void testSetDidNotAudiblyAlert() {
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ record.setAudiblyAlerted(false);
+
+ assertEquals(-1, record.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testSetAudiblyAlerted() {
StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
@@ -814,6 +823,6 @@
record.setAudiblyAlerted(true);
- assertTrue(record.getAudiblyAlerted());
+ assertNotEquals(-1, record.getLastAudiblyAlertedMs());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
new file mode 100644
index 0000000..ce22788
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
@@ -0,0 +1,225 @@
+/*
+ * 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.server.wm;
+
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.NFC_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_SYSTEM;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.util.SparseBooleanArray;
+
+import com.android.server.wm.LockTaskController.LockTaskToken;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Constructor;
+
+public class KeyguardDisableHandlerTest {
+
+ private KeyguardDisableHandler mKeyguardDisable;
+
+ private boolean mKeyguardEnabled;
+ private SparseBooleanArray mKeyguardSecure = new SparseBooleanArray();
+ private SparseBooleanArray mDpmRequiresPassword = new SparseBooleanArray();
+
+ @Before
+ public void setUp() throws Exception {
+ mKeyguardEnabled = true;
+
+ mKeyguardDisable = new KeyguardDisableHandler(new KeyguardDisableHandler.Injector() {
+ @Override
+ public boolean dpmRequiresPassword(int userId) {
+ return mDpmRequiresPassword.get(userId);
+ }
+
+ @Override
+ public boolean isKeyguardSecure(int userId) {
+ return mKeyguardSecure.get(userId);
+ }
+
+ @Override
+ public int getProfileParentId(int userId) {
+ return userId;
+ }
+
+ @Override
+ public void enableKeyguard(boolean enabled) {
+ mKeyguardEnabled = enabled;
+ }
+ }, mock(Handler.class)) {
+ @Override
+ public void disableKeyguard(IBinder token, String tag, int callingUid, int userId) {
+ super.disableKeyguard(token, tag, callingUid, userId);
+ // In the actual code, the update is posted to the handler thread. Eagerly update
+ // here to simplify the test.
+ updateKeyguardEnabled(userId);
+ }
+
+ @Override
+ public void reenableKeyguard(IBinder token, int callingUid, int userId) {
+ super.reenableKeyguard(token, callingUid, userId);
+ // In the actual code, the update is posted to the handler thread. Eagerly update
+ // here to simplify the test.
+ updateKeyguardEnabled(userId);
+ }
+ };
+ }
+
+ @Test
+ public void starts_enabled() {
+ assertTrue(mKeyguardEnabled);
+ mKeyguardDisable.updateKeyguardEnabled(USER_ALL);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromApp_disables() {
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+ assertFalse(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromApp_secondaryUser_disables() {
+ mKeyguardDisable.setCurrentUser(1);
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag",
+ UserHandle.getUid(1, FIRST_APPLICATION_UID), 1);
+ assertFalse(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromSystem_LockTask_disables() {
+ mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+ assertFalse(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromSystem_genericToken_fails() {
+ try {
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag", SYSTEM_UID, USER_SYSTEM);
+ fail("Expected exception not thrown");
+ } catch (UnsupportedOperationException e) {
+ assertThat(e.getMessage(), containsString("Only apps can use the KeyguardLock API"));
+ }
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromNonApp_genericToken_fails() {
+ try {
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag", NFC_UID, USER_SYSTEM);
+ fail("Expected exception not thrown");
+ } catch (UnsupportedOperationException e) {
+ assertThat(e.getMessage(), containsString("Only apps can use the KeyguardLock API"));
+ }
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromApp_secure_staysEnabled() {
+ configureIsSecure(true, USER_SYSTEM);
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromApp_dpmRequiresPassword_staysEnabled() {
+ configureDpmRequiresPassword(true, USER_SYSTEM);
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromSystem_LockTask_secure_disables() {
+ configureIsSecure(true, USER_SYSTEM);
+ mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+ assertFalse(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromSystem_LockTask_requiresDpm_staysEnabled() {
+ configureDpmRequiresPassword(true, USER_SYSTEM);
+ mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromApp_thenSecure_reenables() {
+ mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM);
+ configureIsSecure(true, USER_SYSTEM);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void disable_fromSystem_LockTask_thenRequiresDpm_reenables() {
+ mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+ configureDpmRequiresPassword(true, USER_SYSTEM);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void user_switch_to_enabledUser_applies_enabled() {
+ mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM);
+ assertFalse("test setup failed", mKeyguardEnabled);
+ mKeyguardDisable.setCurrentUser(1);
+ assertTrue(mKeyguardEnabled);
+ }
+
+ @Test
+ public void user_switch_to_disabledUser_applies_disabled() {
+ mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag",
+ SYSTEM_UID, 1);
+ assertTrue("test setup failed", mKeyguardEnabled);
+ mKeyguardDisable.setCurrentUser(1);
+ assertFalse(mKeyguardEnabled);
+ }
+
+ private void configureIsSecure(boolean secure, int userId) {
+ mKeyguardSecure.put(userId, secure);
+ mKeyguardDisable.updateKeyguardEnabled(userId);
+ }
+
+ private void configureDpmRequiresPassword(boolean requiresPassword, int userId) {
+ mDpmRequiresPassword.put(userId, requiresPassword);
+ mKeyguardDisable.updateKeyguardEnabled(userId);
+ }
+
+ private LockTaskToken createLockTaskToken() {
+ try {
+ final Constructor<LockTaskToken> constructor =
+ LockTaskToken.class.getDeclaredConstructor();
+ constructor.setAccessible(true);
+ return constructor.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 6259fa6..a9f150b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -560,19 +560,20 @@
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// THEN keyguard should be disabled
- verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+ verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
// WHEN keyguard is enabled for lock task mode
mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
// THEN keyguard should be enabled
- verify(mWindowManager).reenableKeyguard(any(IBinder.class));
+ verify(mWindowManager).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
// WHEN keyguard is disabled again for lock task mode
mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
// THEN keyguard should be disabled
- verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString());
+ verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString(),
+ eq(TEST_USER_ID));
}
@Test
@@ -653,7 +654,7 @@
private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception {
// THEN the keyguard should have been disabled
- verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+ verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
// THEN the status bar should have been disabled
verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
eq(mContext.getPackageName()));
@@ -668,7 +669,7 @@
private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
// THEN the keyguard should have been disabled
- verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class));
+ verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
// THEN the status bar should have been disabled
verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
any(IBinder.class), eq(mContext.getPackageName()));
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 36d0188..2820836 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -518,6 +518,7 @@
private final Bundle mExtras;
private final Bundle mIntentExtras;
private final long mCreationTimeMillis;
+ private final CallIdentification mCallIdentification;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -699,6 +700,12 @@
}
/**
+ * The display name for the caller.
+ * <p>
+ * This is the name as reported by the {@link ConnectionService} associated with this call.
+ * The name reported by a {@link CallScreeningService} can be retrieved using
+ * {@link CallIdentification#getName()}.
+ *
* @return The display name for the caller.
*/
public String getCallerDisplayName() {
@@ -814,6 +821,23 @@
return mCreationTimeMillis;
}
+ /**
+ * Returns {@link CallIdentification} information provided by a
+ * {@link CallScreeningService} for this call.
+ * <p>
+ * {@link InCallService} implementations should display the {@link CallIdentification} for
+ * calls. The name of the call screening service is provided in
+ * {@link CallIdentification#getCallScreeningAppName()} and should be used to attribute the
+ * call identification information.
+ *
+ * @return The {@link CallIdentification} if it was provided by a
+ * {@link CallScreeningService}, or {@code null} if no {@link CallScreeningService} has
+ * provided {@link CallIdentification} information for the call.
+ */
+ public @Nullable CallIdentification getCallIdentification() {
+ return mCallIdentification;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -834,7 +858,8 @@
Objects.equals(mStatusHints, d.mStatusHints) &&
areBundlesEqual(mExtras, d.mExtras) &&
areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
- Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis);
+ Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
+ Objects.equals(mCallIdentification, d.mCallIdentification);
}
return false;
}
@@ -855,7 +880,8 @@
mStatusHints,
mExtras,
mIntentExtras,
- mCreationTimeMillis);
+ mCreationTimeMillis,
+ mCallIdentification);
}
/** {@hide} */
@@ -875,7 +901,8 @@
StatusHints statusHints,
Bundle extras,
Bundle intentExtras,
- long creationTimeMillis) {
+ long creationTimeMillis,
+ CallIdentification callIdentification) {
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -892,6 +919,7 @@
mExtras = extras;
mIntentExtras = intentExtras;
mCreationTimeMillis = creationTimeMillis;
+ mCallIdentification = callIdentification;
}
/** {@hide} */
@@ -912,7 +940,8 @@
parcelableCall.getStatusHints(),
parcelableCall.getExtras(),
parcelableCall.getIntentExtras(),
- parcelableCall.getCreationTimeMillis());
+ parcelableCall.getCreationTimeMillis(),
+ parcelableCall.getCallIdentification());
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl b/telecomm/java/android/telecom/CallIdentification.aidl
similarity index 63%
copy from telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
copy to telecomm/java/android/telecom/CallIdentification.aidl
index 4c289ac..532535c 100644
--- a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
+++ b/telecomm/java/android/telecom/CallIdentification.aidl
@@ -1,11 +1,11 @@
/*
- * 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 +14,9 @@
* limitations under the License.
*/
-package com.android.internal.telephony.rcs;
+package android.telecom;
-interface IRcs {
- // RcsManager APIs
- void deleteThread(int threadId);
-
- // RcsThread APIs
- int getMessageCount(int rcsThreadId);
-}
\ No newline at end of file
+/**
+ * {@hide}
+ */
+parcelable CallIdentification;
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
new file mode 100644
index 0000000..97af06c
--- /dev/null
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -0,0 +1,433 @@
+/*
+ * 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.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about an incoming or outgoing {@link Call} provided by a
+ * {@link CallScreeningService}.
+ * <p>
+ * Call identified information is consumed by the {@link InCallService dialer} app to provide the
+ * user with more information about a call. This can include information such as the name of the
+ * caller, address, etc. Call identification information is persisted to the
+ * {@link android.provider.CallLog}.
+ */
+public final class CallIdentification implements Parcelable {
+ /**
+ * Builder for {@link CallIdentification} instances.
+ * <p>
+ * A {@link CallScreeningService} uses this class to create new instances of
+ * {@link CallIdentification} for a screened call.
+ */
+ public static class Builder {
+ private String mName;
+ private String mDescription;
+ private String mDetails;
+ private Icon mPhoto;
+ private int mNuisanceConfidence = CallIdentification.CONFIDENCE_UNKNOWN;
+ private String mPackageName;
+ private String mAppName;
+
+ /**
+ * Default builder constructor.
+ */
+ public Builder() {
+ // Default constructor
+ }
+
+ /**
+ * Create instance of call identification with specified package/app name.
+ *
+ * @param callIdPackageName The package name.
+ * @param callIdAppName The app name.
+ * @hide
+ */
+ public Builder(String callIdPackageName, String callIdAppName) {
+ mPackageName = callIdPackageName;
+ mAppName = callIdAppName;
+ }
+
+ /**
+ * Sets the name associated with the {@link CallIdentification} being built.
+ * <p>
+ * Could be a business name, for example.
+ *
+ * @param name The name associated with the call, or {@code null} if none is provided.
+ * @return Builder instance.
+ */
+ public Builder setName(@Nullable String name) {
+ mName = name;
+ return this;
+ }
+
+ /**
+ * Sets the description associated with the {@link CallIdentification} being built.
+ * <p>
+ * A description of the call as identified by a {@link CallScreeningService}. The
+ * description is typically presented by Dialer apps after the
+ * {@link CallIdentification#getName() name} to provide a short piece of relevant
+ * information about the call. This could include a location, address, or a message
+ * regarding the potential nature of the call (e.g. potential telemarketer).
+ *
+ * @param description The call description, or {@code null} if none is provided.
+ * @return Builder instance.
+ */
+ public Builder setDescription(@Nullable String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Sets the details associated with the {@link CallIdentification} being built.
+ * <p>
+ * The details is typically presented by Dialer apps after the
+ * {@link CallIdentification#getName() name} and
+ * {@link CallIdentification#getDescription() description} to provide further clarifying
+ * information about the call. This could include, for example, the opening hours of a
+ * business, or a stats about the number of times a call has been reported as spam.
+ *
+ * @param details The call details, or {@code null} if none is provided.
+ * @return Builder instance.
+ */
+ public Builder setDetails(@Nullable String details) {
+ mDetails = details;
+ return this;
+ }
+
+ /**
+ * Sets the photo associated with the {@link CallIdentification} being built.
+ * <p>
+ * This could be, for example, a business logo, or a photo of the caller.
+ *
+ * @param photo The photo associated with the call, or {@code null} if none was provided.
+ * @return Builder instance.
+ */
+ public Builder setPhoto(@Nullable Icon photo) {
+ mPhoto = photo;
+ return this;
+ }
+
+ /**
+ * Sets the nuisance confidence with the {@link CallIdentification} being built.
+ * <p>
+ * This can be used to specify how confident the {@link CallScreeningService} is that a call
+ * is or is not a nuisance call.
+ *
+ * @param nuisanceConfidence The nuisance confidence.
+ * @return The builder.
+ */
+ public Builder setNuisanceConfidence(@NuisanceConfidence int nuisanceConfidence) {
+ mNuisanceConfidence = nuisanceConfidence;
+ return this;
+ }
+
+ /**
+ * Creates a new instance of {@link CallIdentification} based on the parameters set in this
+ * builder.
+ *
+ * @return {@link CallIdentification} instance.
+ */
+ public CallIdentification build() {
+ return new CallIdentification(mName, mDescription, mDetails, mPhoto,
+ mNuisanceConfidence, mPackageName, mAppName);
+ }
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "CONFIDENCE_" },
+ value = {CONFIDENCE_NUISANCE, CONFIDENCE_LIKELY_NUISANCE, CONFIDENCE_UNKNOWN,
+ CONFIDENCE_LIKELY_NOT_NUISANCE, CONFIDENCE_NOT_NUISANCE})
+ public @interface NuisanceConfidence {}
+
+ /**
+ * Call has been identified as a nuisance call.
+ * <p>
+ * Returned from {@link #getNuisanceConfidence()} to indicate that a
+ * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+ * nuisance call.
+ */
+ public static final int CONFIDENCE_NUISANCE = 2;
+
+ /**
+ * Call has been identified as a likely nuisance call.
+ * <p>
+ * Returned from {@link #getNuisanceConfidence()} to indicate that a
+ * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+ * nuisance call.
+ */
+ public static final int CONFIDENCE_LIKELY_NUISANCE = 1;
+
+ /**
+ * Call could not be classified as nuisance or non-nuisance.
+ * <p>
+ * Returned from {@link #getNuisanceConfidence()} to indicate that a
+ * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+ * nuisance call.
+ */
+ public static final int CONFIDENCE_UNKNOWN = 0;
+
+ /**
+ * Call has been identified as not likely to be a nuisance call.
+ * <p>
+ * Returned from {@link #getNuisanceConfidence()} to indicate that a
+ * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+ * nuisance call.
+ */
+ public static final int CONFIDENCE_LIKELY_NOT_NUISANCE = -1;
+
+ /**
+ * Call has been identified as not a nuisance call.
+ * <p>
+ * Returned from {@link #getNuisanceConfidence()} to indicate that a
+ * {@link CallScreeningService} to indicate how confident it is that a call is or is not a
+ * nuisance call.
+ */
+ public static final int CONFIDENCE_NOT_NUISANCE = -2;
+
+ /**
+ * Default constructor for {@link CallIdentification}.
+ *
+ * @param name The name.
+ * @param description The description.
+ * @param details The details.
+ * @param photo The photo.
+ * @param nuisanceConfidence Confidence that this is a nuisance call.
+ * @hide
+ */
+ private CallIdentification(@Nullable String name, @Nullable String description,
+ @Nullable String details, @Nullable Icon photo,
+ @NuisanceConfidence int nuisanceConfidence) {
+ this(name, description, details, photo, nuisanceConfidence, null, null);
+ }
+
+ /**
+ * Default constructor for {@link CallIdentification}.
+ *
+ * @param name The name.
+ * @param description The description.
+ * @param details The details.
+ * @param photo The photo.
+ * @param nuisanceConfidence Confidence that this is a nuisance call.
+ * @param callScreeningPackageName Package name of the {@link CallScreeningService} which
+ * provided the call identification.
+ * @param callScreeningAppName App name of the {@link CallScreeningService} which provided the
+ * call identification.
+ * @hide
+ */
+ private CallIdentification(@Nullable String name, @Nullable String description,
+ @Nullable String details, @Nullable Icon photo,
+ @NuisanceConfidence int nuisanceConfidence, @NonNull String callScreeningPackageName,
+ @NonNull String callScreeningAppName) {
+ mName = name;
+ mDescription = description;
+ mDetails = details;
+ mPhoto = photo;
+ mNuisanceConfidence = nuisanceConfidence;
+ mCallScreeningAppName = callScreeningPackageName;
+ mCallScreeningPackageName = callScreeningAppName;
+ }
+
+ private String mName;
+ private String mDescription;
+ private String mDetails;
+ private Icon mPhoto;
+ private int mNuisanceConfidence;
+ private String mCallScreeningPackageName;
+ private String mCallScreeningAppName;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(mName);
+ parcel.writeString(mDescription);
+ parcel.writeString(mDetails);
+ parcel.writeParcelable(mPhoto, 0);
+ parcel.writeInt(mNuisanceConfidence);
+ parcel.writeString(mCallScreeningPackageName);
+ parcel.writeString(mCallScreeningAppName);
+ }
+
+ /**
+ * Responsible for creating CallIdentification objects for deserialized Parcels.
+ */
+ public static final Parcelable.Creator<CallIdentification> CREATOR =
+ new Parcelable.Creator<CallIdentification> () {
+
+ @Override
+ public CallIdentification createFromParcel(Parcel source) {
+ String name = source.readString();
+ String description = source.readString();
+ String details = source.readString();
+ Icon photo = source.readParcelable(ClassLoader.getSystemClassLoader());
+ int nuisanceConfidence = source.readInt();
+ String callScreeningPackageName = source.readString();
+ String callScreeningAppName = source.readString();
+ return new CallIdentification(name, description, details, photo,
+ nuisanceConfidence, callScreeningPackageName, callScreeningAppName);
+ }
+
+ @Override
+ public CallIdentification[] newArray(int size) {
+ return new CallIdentification[size];
+ }
+ };
+
+ /**
+ * The name associated with the number.
+ * <p>
+ * The name of the call as identified by a {@link CallScreeningService}. Could be a business
+ * name, for example.
+ *
+ * @return The name associated with the number, or {@code null} if none was provided.
+ */
+ public final @Nullable String getName() {
+ return mName;
+ }
+
+ /**
+ * Description of the call.
+ * <p>
+ * A description of the call as identified by a {@link CallScreeningService}. The description
+ * is typically presented by Dialer apps after the {@link #getName() name} to provide a short
+ * piece of relevant information about the call. This could include a location, address, or a
+ * message regarding the potential nature of the call (e.g. potential telemarketer).
+ *
+ * @return The call description, or {@code null} if none was provided.
+ */
+ public final @Nullable String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Details of the call.
+ * <p>
+ * Details of the call as identified by a {@link CallScreeningService}. The details
+ * are typically presented by Dialer apps after the {@link #getName() name} and
+ * {@link #getDescription() description} to provide further clarifying information about the
+ * call. This could include, for example, the opening hours of a business, or stats about
+ * the number of times a call has been reported as spam.
+ *
+ * @return The call details, or {@code null} if none was provided.
+ */
+ public final @Nullable String getDetails() {
+ return mDetails;
+ }
+
+ /**
+ * Photo associated with the call.
+ * <p>
+ * A photo associated with the call as identified by a {@link CallScreeningService}. This
+ * could be, for example, a business logo, or a photo of the caller.
+ *
+ * @return The photo associated with the call, or {@code null} if none was provided.
+ */
+ public final @Nullable Icon getPhoto() {
+ return mPhoto;
+ }
+
+ /**
+ * Indicates the likelihood that this call is a nuisance call.
+ * <p>
+ * How likely the call is a nuisance call, as identified by a {@link CallScreeningService}.
+ *
+ * @return The nuisance confidence.
+ */
+ public final @NuisanceConfidence
+ int getNuisanceConfidence() {
+ return mNuisanceConfidence;
+ }
+
+ /**
+ * The package name of the {@link CallScreeningService} which provided the
+ * {@link CallIdentification}.
+ * <p>
+ * A {@link CallScreeningService} may not set this property; it is set by the system.
+ * @return the package name
+ */
+ public final @NonNull String getCallScreeningPackageName() {
+ return mCallScreeningPackageName;
+ }
+
+ /**
+ * The {@link android.content.pm.PackageManager#getApplicationLabel(ApplicationInfo) name} of
+ * the {@link CallScreeningService} which provided the {@link CallIdentification}.
+ * <p>
+ * A {@link CallScreeningService} may not set this property; it is set by the system.
+ *
+ * @return The name of the app.
+ */
+ public final @NonNull String getCallScreeningAppName() {
+ return mCallScreeningAppName;
+ }
+
+ /**
+ * Set the package name of the {@link CallScreeningService} which provided this information.
+ *
+ * @param callScreeningPackageName The package name.
+ * @hide
+ */
+ public void setCallScreeningPackageName(@NonNull String callScreeningPackageName) {
+ mCallScreeningPackageName = callScreeningPackageName;
+ }
+
+ /**
+ * Set the app name of the {@link CallScreeningService} which provided this information.
+ *
+ * @param callScreeningAppName The app name.
+ * @hide
+ */
+ public void setCallScreeningAppName(@NonNull String callScreeningAppName) {
+ mCallScreeningAppName = callScreeningAppName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CallIdentification that = (CallIdentification) o;
+ // Note: mPhoto purposely omit as no good comparison exists.
+ return mNuisanceConfidence == that.mNuisanceConfidence
+ && Objects.equals(mName, that.mName)
+ && Objects.equals(mDescription, that.mDescription)
+ && Objects.equals(mDetails, that.mDetails)
+ && Objects.equals(mCallScreeningAppName, that.mCallScreeningAppName)
+ && Objects.equals(mCallScreeningPackageName, that.mCallScreeningPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mDescription, mDetails, mPhoto, mNuisanceConfidence,
+ mCallScreeningAppName, mCallScreeningPackageName);
+ }
+}
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 6628743..be96b3c 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.app.Service;
import android.content.ComponentName;
@@ -46,6 +47,15 @@
* </service>
* }
* </pre>
+ * <p>
+ * A CallScreeningService performs two functions:
+ * <ol>
+ * <li>Call blocking/screening - the service can choose which calls will ring on the user's
+ * device, and which will be silently sent to voicemail.</li>
+ * <li>Call identification - the service can optionally provide {@link CallIdentification}
+ * information about a {@link Call.Details call} which will be shown to the user in the
+ * Dialer app.</li>
+ * </ol>
*/
public abstract class CallScreeningService extends Service {
/**
@@ -229,16 +239,23 @@
*
* @param callDetails Information about a new incoming call, see {@link Call.Details}.
*/
- public abstract void onScreenCall(Call.Details callDetails);
+ public abstract void onScreenCall(@NonNull Call.Details callDetails);
/**
* Responds to the given call, either allowing it or disallowing it.
+ * <p>
+ * The {@link CallScreeningService} calls this method to inform the system whether the call
+ * should be silently blocked or not.
*
* @param callDetails The call to allow.
+ * <p>
+ * Must be the same {@link Call.Details call} which was provided to the
+ * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
* @param response The {@link CallScreeningService.CallResponse} which contains information
* about how to respond to a call.
*/
- public final void respondToCall(Call.Details callDetails, CallResponse response) {
+ public final void respondToCall(@NonNull Call.Details callDetails,
+ @NonNull CallResponse response) {
try {
if (response.getDisallowCall()) {
mCallScreeningAdapter.disallowCall(
@@ -253,4 +270,32 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Provide {@link CallIdentification} information about a {@link Call.Details call}.
+ * <p>
+ * The {@link CallScreeningService} calls this method to provide information it has identified
+ * about a {@link Call.Details call}. This information will potentially be shown to the user
+ * in the {@link InCallService dialer} app. It will be logged to the
+ * {@link android.provider.CallLog}.
+ * <p>
+ * A {@link CallScreeningService} should only call this method for calls for which it is able to
+ * provide some {@link CallIdentification} for. {@link CallIdentification} instances with no
+ * fields set will be ignored by the system.
+ *
+ * @param callDetails The call to provide information for.
+ * <p>
+ * Must be the same {@link Call.Details call} which was provided to the
+ * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}.
+ * @param identification An instance of {@link CallIdentification} with information about the
+ * {@link Call.Details call}.
+ */
+ public final void provideCallIdentification(@NonNull Call.Details callDetails,
+ @NonNull CallIdentification identification) {
+ try {
+ mCallScreeningAdapter.provideCallIdentification(callDetails.getTelecomCallId(),
+ identification);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 3ad0f0c..911786e 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Build;
@@ -62,6 +63,7 @@
private final Bundle mIntentExtras;
private final Bundle mExtras;
private final long mCreationTimeMillis;
+ private final CallIdentification mCallIdentification;
public ParcelableCall(
String id,
@@ -89,7 +91,8 @@
List<String> conferenceableCallIds,
Bundle intentExtras,
Bundle extras,
- long creationTimeMillis) {
+ long creationTimeMillis,
+ CallIdentification callIdentification) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -116,6 +119,7 @@
mIntentExtras = intentExtras;
mExtras = extras;
mCreationTimeMillis = creationTimeMillis;
+ mCallIdentification = callIdentification;
}
/** The unique ID of the call. */
@@ -133,7 +137,7 @@
* Reason for disconnection, as described by {@link android.telecomm.DisconnectCause}. Valid
* when call state is {@link CallState#DISCONNECTED}.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public DisconnectCause getDisconnectCause() {
return mDisconnectCause;
}
@@ -159,13 +163,13 @@
}
/** The time that the call switched to the active state. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public long getConnectTimeMillis() {
return mConnectTimeMillis;
}
/** The endpoint to which the call is connected. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public Uri getHandle() {
return mHandle;
}
@@ -305,8 +309,17 @@
return mCreationTimeMillis;
}
+ /**
+ * Contains call identification information returned by a {@link CallScreeningService}.
+ * @return The {@link CallIdentification} for this call, or {@code null} if a
+ * {@link CallScreeningService} did not provide information.
+ */
+ public @Nullable CallIdentification getCallIdentification() {
+ return mCallIdentification;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final Parcelable.Creator<ParcelableCall> CREATOR =
new Parcelable.Creator<ParcelableCall> () {
@Override
@@ -342,6 +355,7 @@
boolean isRttCallChanged = source.readByte() == 1;
ParcelableRttCall rttCall = source.readParcelable(classLoader);
long creationTimeMillis = source.readLong();
+ CallIdentification callIdentification = source.readParcelable(classLoader);
return new ParcelableCall(
id,
state,
@@ -368,7 +382,8 @@
conferenceableCallIds,
intentExtras,
extras,
- creationTimeMillis);
+ creationTimeMillis,
+ callIdentification);
}
@Override
@@ -413,6 +428,7 @@
destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0));
destination.writeParcelable(mRttCall, 0);
destination.writeLong(mCreationTimeMillis);
+ destination.writeParcelable(mCallIdentification, 0);
}
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 49030f5..6c4b1af8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -23,6 +23,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
+import android.app.role.RoleManagerCallback;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +32,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -42,6 +44,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Provides access to information about active calls and registration/call-management functionality.
@@ -476,7 +479,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";
@@ -1188,8 +1191,12 @@
* Requires permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
*
* @hide
+ * @deprecated Use
+ * {@link android.app.role.RoleManager#addRoleHolderAsUser(String, String, UserHandle, Executor,
+ * RoleManagerCallback)} instead.
*/
@SystemApi
+ @Deprecated
@RequiresPermission(allOf = {
android.Manifest.permission.MODIFY_PHONE_STATE,
android.Manifest.permission.WRITE_SECURE_SETTINGS})
@@ -1221,79 +1228,6 @@
}
/**
- * Used to trigger display of the ChangeDefaultCallScreeningApp activity to prompt the user to
- * change the call screening app.
- *
- * A {@link SecurityException} will be thrown if calling package name doesn't match the package
- * of the passed {@link ComponentName}
- *
- * @param componentName to verify that the calling package name matches the package of the
- * passed ComponentName.
- */
- public void requestChangeDefaultCallScreeningApp(@NonNull ComponentName componentName) {
- try {
- if (isServiceConnected()) {
- getTelecomService().requestChangeDefaultCallScreeningApp(componentName, mContext
- .getOpPackageName());
- }
- } catch (RemoteException e) {
- Log.e(TAG,
- "RemoteException calling ITelecomService#requestChangeDefaultCallScreeningApp.",
- e);
- }
- }
-
- /**
- * Used to verify that the passed ComponentName is default call screening app.
- *
- * @param componentName to verify that the package of the passed ComponentName matched the default
- * call screening packageName.
- *
- * @return {@code true} if the passed componentName matches the default call screening's, {@code
- * false} if the passed componentName is null, or it doesn't match default call screening's.
- */
- public boolean isDefaultCallScreeningApp(ComponentName componentName) {
- try {
- if (isServiceConnected()) {
- return getTelecomService().isDefaultCallScreeningApp(componentName);
- }
- } catch (RemoteException e) {
- Log.e(TAG,
- "RemoteException calling ITelecomService#isDefaultCallScreeningApp.",
- e);
- }
- return false;
- }
-
- /**
- * Used to set the default call screening package.
- *
- * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} Requires
- * permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
- *
- * A {@link IllegalArgumentException} will be thrown if the specified package and component name
- * of {@link ComponentName} does't exist, or the specified component of {@link ComponentName}
- * does't have {@link android.Manifest.permission#BIND_SCREENING_SERVICE}.
- *
- * @param componentName to set the default call screening to.
- * @hide
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.MODIFY_PHONE_STATE,
- android.Manifest.permission.WRITE_SECURE_SETTINGS
- })
- public void setDefaultCallScreeningApp(ComponentName componentName) {
- try {
- if (isServiceConnected()) {
- getTelecomService().setDefaultCallScreeningApp(componentName);
- }
- } catch (RemoteException e) {
- Log.e(TAG,
- "RemoteException calling ITelecomService#setDefaultCallScreeningApp.", e);
- }
- }
-
- /**
* Return whether a given phone number is the configured voicemail number for a
* particular phone account.
*
diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
index d255ed1..a86c830 100644
--- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telecom;
import android.content.ComponentName;
+import android.telecom.CallIdentification;
/**
* Internal remote callback interface for call screening services.
@@ -34,4 +35,8 @@
boolean shouldAddToCallLog,
boolean shouldShowNotification,
in ComponentName componentName);
+
+ void provideCallIdentification(
+ String callId,
+ in CallIdentification callIdentification);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d64efea..50204e7 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -256,21 +256,6 @@
boolean setDefaultDialer(in String packageName);
/**
- * @see TelecomServiceImpl#requestChangeDefaultCallScreeningApp
- */
- void requestChangeDefaultCallScreeningApp(in ComponentName componentNamem, String callingPackage);
-
- /**
- * @see TelecomServiceImpl#isDefaultCallScreeningApp
- */
- boolean isDefaultCallScreeningApp(in ComponentName componentName);
-
- /**
- * @see TelecomServiceImpl#setDefaultCallScreeningApp
- */
- void setDefaultCallScreeningApp(in ComponentName componentName);
-
- /**
* @see TelecomServiceImpl#createManageBlockedNumbersIntent
**/
Intent createManageBlockedNumbersIntent();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cfe134fd..fd14916 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}
@@ -2658,12 +2658,9 @@
//6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
//8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
});
- sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE,
- new String[] {
- String.valueOf(false) + ": 7",
- //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
- String.valueOf(true) + ": 8"
- //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+ sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE, new String[] {
+ String.valueOf(false) + ": 7", //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+ String.valueOf(true) + ": 8" //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
});
sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
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/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 4354314..4bca404 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.CallSuper;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -53,7 +52,6 @@
private final String TAG = NetworkService.class.getSimpleName();
public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
- public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER = 1;
private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER = 2;
@@ -81,7 +79,7 @@
* must extend this class to support network connection. Note that each instance of network
* service is associated with one physical SIM slot.
*/
- public class NetworkServiceProvider {
+ public abstract class NetworkServiceProvider implements AutoCloseable {
private final int mSlotId;
private final List<INetworkServiceCallback>
@@ -137,12 +135,12 @@
}
/**
- * Called when the instance of network service is destroyed (e.g. got unbind or binder died).
+ * Called when the instance of network service is destroyed (e.g. got unbind or binder died)
+ * or when the network service provider is removed. The extended class should implement this
+ * method to perform cleanup works.
*/
- @CallSuper
- protected void onDestroy() {
- mNetworkRegistrationStateChangedCallbacks.clear();
- }
+ @Override
+ public abstract void close();
}
private class NetworkServiceHandler extends Handler {
@@ -168,7 +166,7 @@
case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
// If the service provider doesn't exist yet, we try to create it.
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
mServiceMap.remove(slotId);
}
break;
@@ -176,7 +174,7 @@
for (int i = 0; i < mServiceMap.size(); i++) {
serviceProvider = mServiceMap.get(i);
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
}
}
mServiceMap.clear();
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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 387453f..8c65eb6 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -572,7 +572,6 @@
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
- * IS_EMBEDDED should always be true.
* <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
* @hide
*/
@@ -2379,18 +2378,32 @@
}
/**
- * Set opportunistic by simInfo index
+ * Set whether a subscription is opportunistic, that is, whether the network it connects
+ * to has limited coverage. For example, CBRS. Setting a subscription opportunistic has
+ * following impacts:
+ * 1) Even if it's active, it will be dormant most of the time. The modem will not try
+ * to scan or camp until it knows an available network is nearby to save power.
+ * 2) Telephony relies on system app or carrier input to notify nearby available networks.
+ * See {@link TelephonyManager#updateAvailableNetworks(List)} for more information.
+ * 3) In multi-SIM devices, when the network is nearby and camped, system may automatically
+ * switch internet data between it and default data subscription, based on carrier
+ * recommendation and its signal strength and metered-ness, etc.
+ *
+ *
+ * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+ * privilege permission of the subscription.
*
* @param opportunistic whether it’s opportunistic subscription.
* @param subId the unique SubscriptionInfo index in database
- * @return the number of records updated
- * @hide
+ * @return {@code true} if the operation is succeed, {@code false} otherwise.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public int setOpportunistic(boolean opportunistic, int subId) {
+ public boolean setOpportunistic(boolean opportunistic, int subId) {
if (VDBG) logd("[setOpportunistic]+ opportunistic:" + opportunistic + " subId:" + subId);
return setSubscriptionPropertyHelper(subId, "setOpportunistic",
- (iSub)-> iSub.setOpportunistic(opportunistic, subId));
+ (iSub)-> iSub.setOpportunistic(
+ opportunistic, subId, mContext.getOpPackageName())) == 1;
}
/**
@@ -2510,18 +2523,26 @@
}
/**
- * Set metered by simInfo index
+ * Set if a subscription is metered or not. Similar to Wi-Fi, metered means
+ * user may be charged more if more data is used.
+ *
+ * By default all Cellular networks are considered metered. System or carrier privileged apps
+ * can set a subscription un-metered which will be considered when system switches data between
+ * primary subscription and opportunistic subscription.
+ *
+ * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+ * privilege permission of the subscription.
*
* @param isMetered whether it’s a metered subscription.
* @param subId the unique SubscriptionInfo index in database
- * @return the number of records updated
- * @hide
+ * @return {@code true} if the operation is succeed, {@code false} otherwise.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public int setMetered(boolean isMetered, int subId) {
+ public boolean setMetered(boolean isMetered, int subId) {
if (VDBG) logd("[setIsMetered]+ isMetered:" + isMetered + " subId:" + subId);
return setSubscriptionPropertyHelper(subId, "setIsMetered",
- (iSub)-> iSub.setMetered(isMetered, subId));
+ (iSub)-> iSub.setMetered(isMetered, subId, mContext.getOpPackageName())) == 1;
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 348ab2a..e0632b1 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;
@@ -6665,8 +6671,8 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCarrierPrivilegeStatus(subId) ==
- CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ return telephony.getCarrierPrivilegeStatus(subId)
+ == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
} catch (RemoteException ex) {
Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
@@ -8950,6 +8956,23 @@
}
/**
+ * Action set from carrier signalling broadcast receivers to reset all carrier actions
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @hide
+ */
+ public void carrierActionResetAll(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.carrierActionResetAll(subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#carrierActionResetAll", e);
+ }
+ }
+
+ /**
* Get aggregated video call data usage since boot.
* Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
*
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 1db5850..74d1e83 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -16,7 +16,6 @@
package android.telephony.data;
-import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,7 +59,6 @@
private static final String TAG = DataService.class.getSimpleName();
public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
- public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
/** {@hide} */
@IntDef(prefix = "REQUEST_REASON_", value = {
@@ -116,7 +114,7 @@
* must extend this class to support data connection. Note that each instance of data service
* provider is associated with one physical SIM slot.
*/
- public class DataServiceProvider {
+ public abstract class DataServiceProvider implements AutoCloseable {
private final int mSlotId;
@@ -250,12 +248,12 @@
}
/**
- * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+ * Called when the instance of data service is destroyed (e.g. got unbind or binder died)
+ * or when the data service provider is removed. The extended class should implement this
+ * method to perform cleanup works.
*/
- @CallSuper
- protected void onDestroy() {
- mDataCallListChangedCallbacks.clear();
- }
+ @Override
+ public abstract void close();
}
private static final class SetupDataCallRequest {
@@ -345,7 +343,7 @@
break;
case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
mServiceMap.remove(slotId);
}
break;
@@ -353,7 +351,7 @@
for (int i = 0; i < mServiceMap.size(); i++) {
serviceProvider = mServiceMap.get(i);
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
}
}
mServiceMap.clear();
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 57d9cce..45b4849 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -151,7 +151,7 @@
/**
* Called when the qualified networks updater is removed. The extended class should
- * implement this method to perform clean up works.
+ * implement this method to perform cleanup works.
*/
@Override
public abstract void close();
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..79d4732 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/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 65d1a920..65eedb8 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -162,7 +162,7 @@
* @param subId the unique SubscriptionInfo index in database
* @return the number of records updated
*/
- int setOpportunistic(boolean opportunistic, int subId);
+ int setOpportunistic(boolean opportunistic, int subId, String callingPackage);
/**
* Inform SubscriptionManager that subscriptions in the list are bundled
@@ -190,7 +190,7 @@
* @param subId the unique SubscriptionInfo index in database
* @return the number of records updated
*/
- int setMetered(boolean isMetered, int subId);
+ int setMetered(boolean isMetered, int subId, String callingPackage);
/**
* Set which subscription is preferred for cellular data. It's
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 46366d6..399e255 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1408,6 +1408,14 @@
void carrierActionReportDefaultNetworkStatus(int subId, boolean report);
/**
+ * Action set from carrier signalling broadcast receivers to reset all carrier actions.
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @hide
+ */
+ void carrierActionResetAll(int subId);
+
+ /**
* Get aggregated video call data usage since boot.
* Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
*
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 7f5f03e..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/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 14312cf..369a002 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -20,6 +20,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.IWindowManager;
import junit.framework.TestCase;
@@ -107,7 +108,7 @@
public void testDISABLE_KEYGUARD() {
Binder token = new Binder();
try {
- mWm.disableKeyguard(token, "foo");
+ mWm.disableKeyguard(token, "foo", UserHandle.myUserId());
fail("IWindowManager.disableKeyguard did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -117,7 +118,7 @@
}
try {
- mWm.reenableKeyguard(token);
+ mWm.reenableKeyguard(token, UserHandle.myUserId());
fail("IWindowManager.reenableKeyguard did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
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:
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
new file mode 100644
index 0000000..0f29e08
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
@@ -0,0 +1,135 @@
+/*
+ * 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.net.wifi.aware;
+
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.net.TransportInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.Inet6Address;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * Wi-Fi Aware-specific network information. The information can be extracted from the
+ * {@link android.net.NetworkCapabilities} of the network using
+ * {@link NetworkCapabilities#getTransportInfo()}.
+ * The {@link NetworkCapabilities} is provided by the connectivity service to apps, e.g. received
+ * through the
+ * {@link android.net.ConnectivityManager.NetworkCallback#onCapabilitiesChanged(android.net.Network,
+ * android.net.NetworkCapabilities)} callback.
+ * <p>
+ * The Wi-Fi Aware-specific network information include the peer's scoped link-local IPv6 address
+ * for the Wi-Fi Aware link. The scoped link-local IPv6 can then be used to create a
+ * {@link java.net.Socket} connection to the peer.
+ */
+public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable {
+ private Inet6Address mIpv6Addr;
+
+ /** @hide */
+ public WifiAwareNetworkInfo(Inet6Address ipv6Addr) {
+ mIpv6Addr = ipv6Addr;
+ }
+
+ /**
+ * Get the scoped link-local IPv6 address of the Wi-Fi Aware peer (not of the local device!).
+ *
+ * @return An IPv6 address.
+ */
+ @Nullable
+ public Inet6Address getPeerIpv6Addr() {
+ return mIpv6Addr;
+ }
+
+ // parcelable methods
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mIpv6Addr.getAddress());
+ NetworkInterface ni = mIpv6Addr.getScopedInterface();
+ dest.writeString(ni == null ? null : ni.getName());
+ }
+
+ public static final Creator<WifiAwareNetworkInfo> CREATOR =
+ new Creator<WifiAwareNetworkInfo>() {
+ @Override
+ public WifiAwareNetworkInfo createFromParcel(Parcel in) {
+ Inet6Address ipv6Addr;
+ try {
+ byte[] addr = in.createByteArray();
+ String interfaceName = in.readString();
+ NetworkInterface ni = null;
+ if (interfaceName != null) {
+ try {
+ ni = NetworkInterface.getByName(interfaceName);
+ } catch (SocketException e) {
+ e.printStackTrace();
+ }
+ }
+ ipv6Addr = Inet6Address.getByAddress(null, addr, ni);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return new WifiAwareNetworkInfo(ipv6Addr);
+ }
+
+ @Override
+ public WifiAwareNetworkInfo[] newArray(int size) {
+ return new WifiAwareNetworkInfo[size];
+ }
+ };
+
+
+ // object methods
+
+ @Override
+ public String toString() {
+ return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).toString();
+ }
+
+ /** @hide */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof WifiAwareNetworkInfo)) {
+ return false;
+ }
+
+ WifiAwareNetworkInfo lhs = (WifiAwareNetworkInfo) obj;
+ return Objects.equals(mIpv6Addr, lhs.mIpv6Addr);
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIpv6Addr);
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 272f727..fd383ef 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.net.MacAddress;
import android.net.wifi.RttManager;
import android.os.Build;
import android.os.Handler;
@@ -50,6 +51,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
import java.util.List;
/**
@@ -1245,4 +1248,31 @@
sessionCaptor.getValue().createNetworkSpecifierPassphrase(role, someMac, passphrase);
}
}
+
+ // WifiAwareNetworkInfo tests
+
+ @Test
+ public void testWifiAwareNetworkCapabilitiesParcel() throws UnknownHostException {
+ final Inet6Address inet6 = MacAddress.fromString(
+ "11:22:33:44:55:66").getLinkLocalIpv6FromEui48Mac();
+ // note: dummy scope = 5
+ final Inet6Address inet6Scoped = Inet6Address.getByAddress(null, inet6.getAddress(), 5);
+
+ assertEquals(inet6Scoped.toString(), "/fe80::1322:33ff:fe44:5566%5");
+ WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped);
+
+ Parcel parcelW = Parcel.obtain();
+ cap.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ WifiAwareNetworkInfo rereadCap =
+ WifiAwareNetworkInfo.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(cap.getPeerIpv6Addr().toString(), "/fe80::1322:33ff:fe44:5566%5");
+ assertEquals(cap.hashCode(), rereadCap.hashCode());
+ }
}