Merge "Reposition the stack to a similar position upon rotation." into qt-dev
diff --git a/Android.bp b/Android.bp
index c3025e8..b4110fa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -920,6 +920,7 @@
"core/java/com/android/internal/util/RingBufferIndices.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
+ "core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/android/net/shared/*.java",
]
diff --git a/api/current.txt b/api/current.txt
index 8ec7594..f9db63d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -287,7 +287,6 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
- field public static final int allowExternalStorageSandbox = 16844201; // 0x10105a9
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -1125,6 +1124,7 @@
field public static final int reqKeyboardType = 16843304; // 0x1010228
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
+ field public static final int requestLegacyExternalStorage = 16844201; // 0x10105a9
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -11859,6 +11859,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PermissionInfo> CREATOR;
field public static final int FLAG_COSTS_MONEY = 1; // 0x1
field public static final int FLAG_HARD_RESTRICTED = 4; // 0x4
+ field public static final int FLAG_IMMUTABLY_RESTRICTED = 16; // 0x10
field public static final int FLAG_INSTALLED = 1073741824; // 0x40000000
field public static final int FLAG_SOFT_RESTRICTED = 8; // 0x8
field public static final int PROTECTION_DANGEROUS = 1; // 0x1
@@ -39144,7 +39145,7 @@
field public static final String CONTENT_ID = "cid";
field public static final String CONTENT_LOCATION = "cl";
field public static final String CONTENT_TYPE = "ct";
- field public static final android.net.Uri CONTENT_URI;
+ field @NonNull public static final android.net.Uri CONTENT_URI;
field public static final String CT_START = "ctt_s";
field public static final String CT_TYPE = "ctt_t";
field public static final String FILENAME = "fn";
@@ -51509,6 +51510,7 @@
method public android.transition.Transition getSharedElementReturnTransition();
method public boolean getSharedElementsUseOverlay();
method @ColorInt public abstract int getStatusBarColor();
+ method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
method public long getTransitionBackgroundFadeDuration();
method public android.transition.TransitionManager getTransitionManager();
method public abstract int getVolumeControlStream();
@@ -51587,6 +51589,7 @@
method public void setSoftInputMode(int);
method public abstract void setStatusBarColor(@ColorInt int);
method public void setSustainedPerformanceMode(boolean);
+ method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
method public abstract void setTitle(CharSequence);
method @Deprecated public abstract void setTitleColor(@ColorInt int);
method public void setTransitionBackgroundFadeDuration(long);
@@ -53614,29 +53617,23 @@
method @NonNull public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(@Nullable java.util.Collection<java.lang.String>);
}
- public final class TextClassifierEvent implements android.os.Parcelable {
+ public abstract class TextClassifierEvent implements android.os.Parcelable {
method public int describeContents();
method @NonNull public int[] getActionIndices();
method @NonNull public String[] getEntityTypes();
method public int getEventCategory();
method @Nullable public android.view.textclassifier.TextClassificationContext getEventContext();
method public int getEventIndex();
- method public long getEventTime();
method public int getEventType();
method @NonNull public android.os.Bundle getExtras();
- method @Nullable public String getLanguage();
- method public int getRelativeSuggestedWordEndIndex();
- method public int getRelativeSuggestedWordStartIndex();
- method public int getRelativeWordEndIndex();
- method public int getRelativeWordStartIndex();
+ method @Nullable public String getModelName();
method @Nullable public String getResultId();
- method public float getScore();
+ method @NonNull public float[] getScores();
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 @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR;
field public static final int TYPE_ACTIONS_GENERATED = 20; // 0x14
field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6
@@ -53658,25 +53655,63 @@
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 @NonNull public android.view.textclassifier.TextClassifierEvent build();
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(@NonNull int...);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityTypes(@NonNull java.lang.String...);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(@Nullable android.view.textclassifier.TextClassificationContext);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setExtras(@NonNull android.os.Bundle);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setLanguage(@Nullable String);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordEndIndex(int);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordStartIndex(int);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setResultId(@Nullable String);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setScore(float);
+ public abstract static class TextClassifierEvent.Builder<T extends android.view.textclassifier.TextClassifierEvent.Builder<T>> {
+ method @NonNull public T setActionIndices(@NonNull int...);
+ method @NonNull public T setEntityTypes(@NonNull java.lang.String...);
+ method @NonNull public T setEventContext(@Nullable android.view.textclassifier.TextClassificationContext);
+ method @NonNull public T setEventIndex(int);
+ method @NonNull public T setExtras(@NonNull android.os.Bundle);
+ method @NonNull public T setModelName(@Nullable String);
+ method @NonNull public T setResultId(@Nullable String);
+ method @NonNull public T setScores(@NonNull float...);
+ }
+
+ public static final class TextClassifierEvent.ConversationActionsEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable {
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent> CREATOR;
+ }
+
+ public static final class TextClassifierEvent.ConversationActionsEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent.Builder> {
+ ctor public TextClassifierEvent.ConversationActionsEvent.Builder(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent build();
+ }
+
+ public static final class TextClassifierEvent.LanguageDetectionEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable {
+ method @Nullable public android.icu.util.ULocale getLocale();
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent> CREATOR;
+ }
+
+ public static final class TextClassifierEvent.LanguageDetectionEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent.Builder> {
+ ctor public TextClassifierEvent.LanguageDetectionEvent.Builder(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent build();
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent.Builder setLocale(@Nullable android.icu.util.ULocale);
+ }
+
+ public static final class TextClassifierEvent.TextLinkifyEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable {
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent> CREATOR;
+ }
+
+ public static final class TextClassifierEvent.TextLinkifyEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent.Builder> {
+ ctor public TextClassifierEvent.TextLinkifyEvent.Builder(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent build();
+ }
+
+ public static final class TextClassifierEvent.TextSelectionEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable {
+ method public int getRelativeSuggestedWordEndIndex();
+ method public int getRelativeSuggestedWordStartIndex();
+ method public int getRelativeWordEndIndex();
+ method public int getRelativeWordStartIndex();
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.TextSelectionEvent> CREATOR;
+ }
+
+ public static final class TextClassifierEvent.TextSelectionEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder> {
+ ctor public TextClassifierEvent.TextSelectionEvent.Builder(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent build();
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeSuggestedWordEndIndex(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeSuggestedWordStartIndex(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeWordEndIndex(int);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeWordStartIndex(int);
}
public final class TextLanguage implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index c031429..b60e850 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1092,11 +1092,14 @@
}
public static final class AppTarget.Builder {
+ ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId);
ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle);
ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo);
method @NonNull public android.app.prediction.AppTarget build();
method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String);
method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
}
public final class AppTargetEvent implements android.os.Parcelable {
@@ -4225,9 +4228,12 @@
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
method public static void setThreadStatsTagRestore();
- field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40
- field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46
- field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42
+ field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f
+ field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80
+ field public static final int TAG_NETWORK_STACK_RANGE_END = -257; // 0xfffffeff
+ field public static final int TAG_NETWORK_STACK_RANGE_START = -768; // 0xfffffd00
+ field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = -241; // 0xffffff0f
+ field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00
}
public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 811ad43..f76b383 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -503,6 +503,8 @@
method @NonNull public android.app.prediction.AppTarget build();
method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String);
method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle);
+ method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo);
}
public final class AppTargetEvent implements android.os.Parcelable {
@@ -1104,6 +1106,7 @@
method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
+ field public static final int SUCCESS = 0; // 0x0
}
public static final class AudioRecord.MetricsConstants {
@@ -1129,22 +1132,6 @@
field public static final String SAMPLE_RATE = "android.media.audiotrack.sampleRate";
}
- public final class BufferingParams implements android.os.Parcelable {
- method public int describeContents();
- method public int getInitialMarkMs();
- method public int getResumePlaybackMarkMs();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
- }
-
- public static class BufferingParams.Builder {
- ctor public BufferingParams.Builder();
- ctor public BufferingParams.Builder(android.media.BufferingParams);
- method public android.media.BufferingParams build();
- method public android.media.BufferingParams.Builder setInitialMarkMs(int);
- method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
- }
-
public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint {
ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size);
ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size);
@@ -1241,8 +1228,10 @@
method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
method public int getStatus();
+ method public boolean removeUidDeviceAffinity(int);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
+ method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public String toLogFriendlyString();
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -1459,9 +1448,6 @@
method public static long getLoopbackRxPackets();
method public static long getLoopbackTxBytes();
method public static long getLoopbackTxPackets();
- field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40
- field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46
- field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42
}
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 55d3fba..d28108c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -268,7 +268,8 @@
TouchGestureClassified touch_gesture_classified = 177;
HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
StyleUIChanged style_ui_changed = 179;
- PrivacyIndicatorsInteracted privacy_indicators_interacted = 180;
+ PrivacyIndicatorsInteracted privacy_indicators_interacted =
+ 180 [(log_from_module) = "permissioncontroller"];
AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181;
NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"];
AppMovedStorageReported app_moved_storage_reported = 183;
@@ -340,6 +341,7 @@
SystemIonHeapSize system_ion_heap_size = 10056;
AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
FaceSettings face_settings = 10058;
+ CoolingDevice cooling_device = 10059;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -410,17 +412,25 @@
* frameworks/base/services/core/java/com/android/server/stats/StatsCompanionService.java
*/
message ThermalThrottlingStateChanged {
+ // The type of temperature being reported (CPU, GPU, SKIN, etc)
optional android.os.TemperatureTypeEnum sensor_type = 1;
+ // Throttling state, this field is DEPRECATED
enum State {
UNKNOWN = 0;
- START = 1;
- STOP = 2;
+ START = 1; // START indicated that throttling was triggered.
+ STOP = 2; // STOP indicates that throttling was cleared.
}
-
optional State state = 2;
+ // Temperature in deci degrees celsius
optional float temperature = 3;
+
+ // Severity of throttling
+ optional android.os.ThrottlingSeverityEnum severity = 4;
+
+ // Thermistor name
+ optional string sensor_name = 5;
}
/**
@@ -3957,8 +3967,7 @@
* Pulls the temperature of various parts of the device.
* The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303.
*
- * Pulled from:
- * frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
+ * Pulled from StatsCompanionService.java
*/
message Temperature {
// The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY, BCL_.
@@ -3970,6 +3979,9 @@
// Temperature in tenths of a degree C.
// For BCL, it is decimillivolt, decimilliamps, and percentage * 10.
optional int32 temperature_deci_celsius = 3;
+
+ // Relative severity of the throttling, see enum definition.
+ optional android.os.ThrottlingSeverityEnum severity = 4;
}
/**
@@ -5947,3 +5959,19 @@
// Whether or not a diverse set of poses are required during enrollment.
optional bool unlock_diversity_required = 6;
}
+
+/**
+ * Logs cooling devices maintained by the kernel.
+ *
+ * Pulled from StatsCompanionService.java
+ */
+message CoolingDevice {
+ // The type of cooling device being reported. Eg. CPU, GPU...
+ optional android.os.CoolingTypeEnum device_location = 1;
+ // The name of the cooling device source. Eg. CPU0
+ optional string device_name = 2;
+ // Current throttle state of the cooling device. The value can any unsigned
+ // integer between 0 and max_state defined in its driver. 0 means device is
+ // not in throttling, higher value means deeper throttling.
+ optional int32 state = 3;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 51839c4..ca73059 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -159,6 +159,9 @@
// temperature
{android::util::TEMPERATURE,
{.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
+ // cooling_device
+ {android::util::COOLING_DEVICE,
+ {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}},
// binder_calls
{android::util::BINDER_CALLS,
{.additiveFields = {4, 5, 6, 8, 12},
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5f972c9..6e935e1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -127,7 +127,6 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
-import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -724,7 +723,7 @@
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
- AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
+ AutofillManager.AutofillClient {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
@@ -1124,12 +1123,6 @@
return this;
}
- /** @hide */
- @Override
- public final ContentCaptureClient getContentCaptureClient() {
- return this;
- }
-
/**
* Register an {@link Application.ActivityLifecycleCallbacks} instance that receives
* lifecycle callbacks for only this Activity.
@@ -6509,12 +6502,6 @@
return getComponentName();
}
- /** @hide */
- @Override
- public final ComponentName contentCaptureClientGetComponentName() {
- return getComponentName();
- }
-
/**
* Retrieve a {@link SharedPreferences} object for accessing preferences
* that are private to this activity. This simply calls the underlying
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0260faa..5e5611b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4549,7 +4549,7 @@
if (!show && !r.stopped) {
performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */,
false /* finalStateRequest */, "handleWindowVisibility");
- } else if (show && r.stopped) {
+ } else if (show && r.getLifecycleState() == ON_STOP) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6f92244..7243624 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -823,9 +823,11 @@
public static final int OP_LEGACY_STORAGE = 87;
/** @hide Accessing accessibility features */
public static final int OP_ACCESS_ACCESSIBILITY = 88;
+ /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
+ public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 89;
+ public static final int _NUM_OP = 90;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1099,6 +1101,8 @@
/** @hide Interact with accessibility. */
@SystemApi
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
+ /** @hide Read device identifiers */
+ public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -1259,6 +1263,7 @@
OP_WRITE_MEDIA_IMAGES, // WRITE_MEDIA_IMAGES
OP_LEGACY_STORAGE, // LEGACY_STORAGE
OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY
+ OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS
};
/**
@@ -1354,6 +1359,7 @@
OPSTR_WRITE_MEDIA_IMAGES,
OPSTR_LEGACY_STORAGE,
OPSTR_ACCESS_ACCESSIBILITY,
+ OPSTR_READ_DEVICE_IDENTIFIERS,
};
/**
@@ -1450,6 +1456,7 @@
"WRITE_MEDIA_IMAGES",
"LEGACY_STORAGE",
"ACCESS_ACCESSIBILITY",
+ "READ_DEVICE_IDENTIFIERS",
};
/**
@@ -1547,6 +1554,7 @@
null, // no permission for OP_WRITE_MEDIA_IMAGES
null, // no permission for OP_LEGACY_STORAGE
null, // no permission for OP_ACCESS_ACCESSIBILITY
+ null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
};
/**
@@ -1644,6 +1652,7 @@
null, // WRITE_MEDIA_IMAGES
null, // LEGACY_STORAGE
null, // ACCESS_ACCESSIBILITY
+ null, // READ_DEVICE_IDENTIFIERS
};
/**
@@ -1740,6 +1749,7 @@
false, // WRITE_MEDIA_IMAGES
false, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
+ false, // READ_DEVICE_IDENTIFIERS
};
/**
@@ -1752,21 +1762,21 @@
AppOpsManager.MODE_ALLOWED, // VIBRATE
AppOpsManager.MODE_ALLOWED, // READ_CONTACTS
AppOpsManager.MODE_ALLOWED, // WRITE_CONTACTS
- AppOpsManager.MODE_DEFAULT, // READ_CALL_LOG
- AppOpsManager.MODE_DEFAULT, // WRITE_CALL_LOG
+ AppOpsManager.MODE_ALLOWED, // READ_CALL_LOG
+ AppOpsManager.MODE_ALLOWED, // WRITE_CALL_LOG
AppOpsManager.MODE_ALLOWED, // READ_CALENDAR
AppOpsManager.MODE_ALLOWED, // WRITE_CALENDAR
AppOpsManager.MODE_ALLOWED, // WIFI_SCAN
AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION
AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS
AppOpsManager.MODE_ALLOWED, // CALL_PHONE
- AppOpsManager.MODE_DEFAULT, // READ_SMS
+ AppOpsManager.MODE_ALLOWED, // READ_SMS
AppOpsManager.MODE_IGNORED, // WRITE_SMS
- AppOpsManager.MODE_DEFAULT, // RECEIVE_SMS
+ AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS
AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST
- AppOpsManager.MODE_DEFAULT, // RECEIVE_MMS
- AppOpsManager.MODE_DEFAULT, // RECEIVE_WAP_PUSH
- AppOpsManager.MODE_DEFAULT, // SEND_SMS
+ AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS
+ AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH
+ AppOpsManager.MODE_ALLOWED, // SEND_SMS
AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
@@ -1800,10 +1810,10 @@
AppOpsManager.MODE_ALLOWED, // READ_PHONE_STATE
AppOpsManager.MODE_ALLOWED, // ADD_VOICEMAIL
AppOpsManager.MODE_ALLOWED, // USE_SIP
- AppOpsManager.MODE_DEFAULT, // PROCESS_OUTGOING_CALLS
+ AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS
AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT
AppOpsManager.MODE_ALLOWED, // BODY_SENSORS
- AppOpsManager.MODE_DEFAULT, // READ_CELL_BROADCASTS
+ AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS
AppOpsManager.MODE_ERRORED, // MOCK_LOCATION
AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE
AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE
@@ -1835,6 +1845,7 @@
AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
+ AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
};
/**
@@ -1934,6 +1945,7 @@
false, // WRITE_MEDIA_IMAGES
false, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
+ false, // READ_DEVICE_IDENTIFIERS
};
/**
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 14c58e7..def1f45 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -27,5 +27,15 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "file_patterns": ["(/|^)ActivityThreadClientTest.java"],
+ "name": "FrameworksMockingCoreTests"
+ },
+ {
+ "file_patterns": ["(/|^)ActivityThreadTest.java"],
+ "name": "FrameworksCoreTests"
+ }
]
}
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index 3f2f209..cd635d6 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -271,7 +271,9 @@
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
- destroy();
+ if (!mIsClosed.get()) {
+ destroy();
+ }
} finally {
super.finalize();
}
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index 4704661..61e4569 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -208,6 +208,7 @@
* @hide
*/
@Deprecated
+ @SystemApi
public Builder(@NonNull AppTargetId id) {
mId = id;
}
@@ -243,7 +244,6 @@
/**
* @deprecated Use the appropriate constructor.
- * @hide
*/
@NonNull
@Deprecated
@@ -258,7 +258,6 @@
/**
* @deprecated Use the appropriate constructor.
- * @hide
*/
@NonNull
@Deprecated
diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java
index 027e152..394a0d6 100644
--- a/core/java/android/app/role/RoleControllerManager.java
+++ b/core/java/android/app/role/RoleControllerManager.java
@@ -52,7 +52,10 @@
private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
+ private static volatile ComponentName sRemoteServiceComponentName;
+
private static final Object sRemoteServicesLock = new Object();
+
/**
* Global remote services (per user) used by all {@link RoleControllerManager managers}.
*/
@@ -62,18 +65,36 @@
@NonNull
private final RemoteService mRemoteService;
- public RoleControllerManager(@NonNull Context context, @NonNull Handler handler) {
+ /**
+ * Initialize the remote service component name once so that we can avoid acquiring the
+ * PackageManagerService lock in constructor.
+ *
+ * @see #createWithInitializedRemoteServiceComponentName(Handler, Context)
+ */
+ public static void initializeRemoteServiceComponentName(@NonNull Context context) {
+ sRemoteServiceComponentName = getRemoteServiceComponentName(context);
+ }
+
+ /**
+ * Create a {@link RoleControllerManager} instance with the initialized remote service component
+ * name so that we can avoid acquiring the PackageManagerService lock in constructor.
+ *
+ * @see #initializeRemoteServiceComponentName(Context)
+ */
+ @NonNull
+ public static RoleControllerManager createWithInitializedRemoteServiceComponentName(
+ @NonNull Handler handler, @NonNull Context context) {
+ return new RoleControllerManager(sRemoteServiceComponentName, handler, context);
+ }
+
+ private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName,
+ @NonNull Handler handler, @NonNull Context context) {
synchronized (sRemoteServicesLock) {
int userId = context.getUserId();
RemoteService remoteService = sRemoteServices.get(userId);
if (remoteService == null) {
- Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
- PackageManager packageManager = context.getPackageManager();
- intent.setPackage(packageManager.getPermissionControllerPackageName());
- ResolveInfo resolveInfo = packageManager.resolveService(intent, 0);
-
remoteService = new RemoteService(context.getApplicationContext(),
- resolveInfo.getComponentInfo().getComponentName(), handler, userId);
+ remoteServiceComponentName, handler, userId);
sRemoteServices.put(userId, remoteService);
}
mRemoteService = remoteService;
@@ -81,7 +102,16 @@
}
public RoleControllerManager(@NonNull Context context) {
- this(context, context.getMainThreadHandler());
+ this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context);
+ }
+
+ @NonNull
+ private static ComponentName getRemoteServiceComponentName(@NonNull Context context) {
+ Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
+ PackageManager packageManager = context.getPackageManager();
+ intent.setPackage(packageManager.getPermissionControllerPackageName());
+ ResolveInfo resolveInfo = packageManager.resolveService(intent, 0);
+ return resolveInfo.getComponentInfo().getComponentName();
}
/**
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index f59bc98..8fb9501 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -24,7 +24,7 @@
import android.util.ArraySet;
import android.util.Log;
import android.view.autofill.AutofillManager;
-import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
+import android.view.autofill.AutofillManager.AutofillClient;
import java.io.PrintWriter;
@@ -73,10 +73,10 @@
public boolean isAugmentedAutofillEnabled(@NonNull Context context) {
if (!augmentedAutofillEnabled) return false;
- final ContentCaptureClient contentCaptureClient = context.getContentCaptureClient();
- if (contentCaptureClient == null) return false;
+ final AutofillClient autofillClient = context.getAutofillClient();
+ if (autofillClient == null) return false;
- final ComponentName component = contentCaptureClient.contentCaptureClientGetComponentName();
+ final ComponentName component = autofillClient.autofillClientGetComponentName();
return whitelistedActivitiesForAugmentedAutofill == null
|| whitelistedActivitiesForAugmentedAutofill.contains(component);
}
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index a646e49..c201e4d 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -283,14 +283,6 @@
return mType == TYPE_ASSERT;
}
- private ContentProviderResult fail(String msg) throws OperationApplicationException {
- if (mFailureAllowed) {
- return new ContentProviderResult(msg);
- } else {
- throw new OperationApplicationException(msg);
- }
- }
-
/**
* Applies this operation using the given provider. The backRefs array is used to resolve any
* back references that were requested using
@@ -307,20 +299,35 @@
*/
public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
int numBackRefs) throws OperationApplicationException {
+ if (mFailureAllowed) {
+ try {
+ return applyInternal(provider, backRefs, numBackRefs);
+ } catch (Exception e) {
+ return new ContentProviderResult(e.getMessage());
+ }
+ } else {
+ return applyInternal(provider, backRefs, numBackRefs);
+ }
+ }
+
+ private ContentProviderResult applyInternal(ContentProvider provider,
+ ContentProviderResult[] backRefs, int numBackRefs)
+ throws OperationApplicationException {
ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
String[] selectionArgs =
resolveSelectionArgsBackReferences(backRefs, numBackRefs);
if (mType == TYPE_INSERT) {
- Uri newUri = provider.insert(mUri, values);
- if (newUri == null) {
- Log.e(TAG, this.toString());
- return fail("Insert into " + mUri + " returned no result");
+ final Uri newUri = provider.insert(mUri, values);
+ if (newUri != null) {
+ return new ContentProviderResult(newUri);
+ } else {
+ throw new OperationApplicationException(
+ "Insert into " + mUri + " returned no result");
}
- return new ContentProviderResult(newUri);
}
- int numRows;
+ final int numRows;
if (mType == TYPE_DELETE) {
numRows = provider.delete(mUri, mSelection, selectionArgs);
} else if (mType == TYPE_UPDATE) {
@@ -346,8 +353,7 @@
final String expectedValue = values.getAsString(projection[i]);
if (!TextUtils.equals(cursorValue, expectedValue)) {
// Throw exception when expected values don't match
- Log.e(TAG, this.toString());
- return fail("Found value " + cursorValue
+ throw new OperationApplicationException("Found value " + cursorValue
+ " when expected " + expectedValue + " for column "
+ projection[i]);
}
@@ -358,13 +364,12 @@
cursor.close();
}
} else {
- Log.e(TAG, this.toString());
throw new IllegalStateException("bad type, " + mType);
}
if (mExpectedCount != null && mExpectedCount != numRows) {
- Log.e(TAG, this.toString());
- return fail("Expected " + mExpectedCount + " rows but actual " + numRows);
+ throw new OperationApplicationException(
+ "Expected " + mExpectedCount + " rows but actual " + numRows);
}
return new ContentProviderResult(numRows);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5a844aa..0ba457e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -70,7 +70,6 @@
import android.view.ViewDebug;
import android.view.WindowManager;
import android.view.autofill.AutofillManager.AutofillClient;
-import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.view.textclassifier.TextClassificationManager;
import java.io.File;
@@ -5414,14 +5413,6 @@
/**
* @hide
*/
- @Nullable
- public ContentCaptureClient getContentCaptureClient() {
- return null;
- }
-
- /**
- * @hide
- */
public final boolean isAutofillCompatibilityEnabled() {
final AutofillOptions options = getAutofillOptions();
return options != null && options.compatModeEnabled;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index deb181f..9bc5f80 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -679,13 +679,13 @@
public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28;
/**
- * Value for {@link #privateFlags}: If {@code true} this app allows
- * shared/external storage media to be a sandboxed view that only contains
- * files owned by the app.
+ * Value for {@link #privateFlags}: If {@code true} this app requests
+ * full external storage access. The request may not be honored due to
+ * policy or other reasons.
*
* @hide
*/
- public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29;
+ public static final int PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE = 1 << 29;
/**
* Value for {@link #privateFlags}: whether this app is pre-installed on the
@@ -723,7 +723,7 @@
PRIVATE_FLAG_HAS_FRAGILE_USER_DATA,
PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
- PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX,
+ PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE,
PRIVATE_FLAG_ODM,
})
@Retention(RetentionPolicy.SOURCE)
@@ -1858,13 +1858,12 @@
}
/**
- * If {@code true} this app allows shared/external storage media to be a
- * sandboxed view that only contains files owned by the app.
+ * If {@code true} this app requested to run in the legacy storage mode.
*
* @hide
*/
- public boolean isExternalStorageSandboxAllowed() {
- return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0;
+ public boolean hasRequestedLegacyExternalStorage() {
+ return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;
}
private boolean isAllowedToUseHiddenApis() {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 6ce6828..d2f0fb3 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1505,13 +1505,14 @@
* allows the app to hold that permission and whitelisting a soft restricted
* permission allows the app to hold the permission in its full, unrestricted form.
*
- * <p>The whitelisted permissions would be applied as the {@link
- * PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}.
+ * <p> Permissions can also be immutably restricted which means that the whitelist
+ * state of the permission can be determined only at install time and cannot be
+ * changed on updated or at a later point via the package manager APIs.
*
- * @param permissions The restricted permissions to whitelist. Pass
- * {@link #RESTRICTED_PERMISSIONS_ALL} to whitelist all permissions and
- * <code>null</code> to clear. If you want to whitelist some permissions
- * (not all) the list must contain at least one permission.
+ * <p>The whitelisted non-immutably restricted permissions would be added to
+ * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}
+ * while the immutably restricted permissions would be added to the {@link
+ * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist}
*
* @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
* @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bdd80e32..0ea5200 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3700,9 +3700,9 @@
}
if (sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowExternalStorageSandbox,
- owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX;
+ R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
+ owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
}
ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index f838900..14340fe 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -341,6 +341,17 @@
public static final int FLAG_SOFT_RESTRICTED = 1<<3;
/**
+ * Flag for {@link #flags}, corresponding to <code>immutablyRestricted</code>
+ * value of {@link android.R.attr#permissionFlags}.
+ *
+ * <p>This permission is restricted immutably which means that its
+ * restriction state may be specified only on the first install of
+ * the app and will stay in this initial whitelist state until
+ * the app is uninstalled.
+ */
+ public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4;
+
+ /**
* Flag for {@link #flags}, indicating that this permission has been
* installed into the system's globally defined permissions.
*/
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index b6c4fe2..68826cb 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -197,7 +197,7 @@
final FileDescriptor queryfd;
try {
queryfd = resNetworkSend((network != null
- ? network.netId : NETID_UNSET), query, query.length, flags);
+ ? network.getNetIdForResolv() : NETID_UNSET), query, query.length, flags);
} catch (ErrnoException e) {
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
return;
@@ -238,7 +238,7 @@
final FileDescriptor queryfd;
try {
queryfd = resNetworkQuery((network != null
- ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags);
+ ? network.getNetIdForResolv() : NETID_UNSET), domain, nsClass, nsType, flags);
} catch (ErrnoException e) {
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
return;
@@ -346,7 +346,8 @@
if (queryIpv6) {
try {
v6fd = resNetworkQuery((network != null
- ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags);
+ ? network.getNetIdForResolv() : NETID_UNSET),
+ domain, CLASS_IN, TYPE_AAAA, flags);
} catch (ErrnoException e) {
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
return;
@@ -365,7 +366,8 @@
if (queryIpv4) {
try {
v4fd = resNetworkQuery((network != null
- ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags);
+ ? network.getNetIdForResolv() : NETID_UNSET),
+ domain, CLASS_IN, TYPE_A, flags);
} catch (ErrnoException e) {
if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
@@ -423,7 +425,7 @@
final FileDescriptor queryfd;
try {
queryfd = resNetworkQuery((network != null
- ? network.netId : NETID_UNSET), domain, CLASS_IN, nsType, flags);
+ ? network.getNetIdForResolv() : NETID_UNSET), domain, CLASS_IN, nsType, flags);
} catch (ErrnoException e) {
executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
return;
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index b8d7cf1..a55d9d0 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -20,6 +20,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.util.TrafficStatsConstants;
+
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
@@ -99,7 +101,8 @@
public boolean requestTime(InetAddress address, int port, int timeout, Network network) {
DatagramSocket socket = null;
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_NTP);
try {
socket = new DatagramSocket();
network.bindSocket(socket);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 49c6f74..4332d8a 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -90,6 +90,42 @@
public static final int UID_TETHERING = -5;
/**
+ * Tag values in this range are reserved for the network stack. The network stack is
+ * running as UID {@link android.os.Process.NETWORK_STACK_UID} when in the mainline
+ * module separate process, and as the system UID otherwise.
+ */
+ /** @hide */
+ @SystemApi
+ public static final int TAG_NETWORK_STACK_RANGE_START = 0xFFFFFD00;
+ /** @hide */
+ @SystemApi
+ public static final int TAG_NETWORK_STACK_RANGE_END = 0xFFFFFEFF;
+
+ /**
+ * Tags between 0xFFFFFF00 and 0xFFFFFFFF are reserved and used internally by system services
+ * like DownloadManager when performing traffic on behalf of an application.
+ */
+ // Please note there is no enforcement of these constants, so do not rely on them to
+ // determine that the caller is a system caller.
+ /** @hide */
+ @SystemApi
+ public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = 0xFFFFFF00;
+ /** @hide */
+ @SystemApi
+ public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = 0xFFFFFF0F;
+
+ /**
+ * Tag values between these ranges are reserved for the network stack to do traffic
+ * on behalf of applications. It is a subrange of the range above.
+ */
+ /** @hide */
+ @SystemApi
+ public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = 0xFFFFFF80;
+ /** @hide */
+ @SystemApi
+ public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = 0xFFFFFF8F;
+
+ /**
* Default tag value for {@link DownloadManager} traffic.
*
* @hide
@@ -127,26 +163,9 @@
*/
public static final int TAG_SYSTEM_APP = 0xFFFFFF05;
+ // TODO : remove this constant when Wifi code is updated
/** @hide */
- @SystemApi
- @TestApi
- public static final int TAG_SYSTEM_DHCP = 0xFFFFFF40;
- /** @hide */
- public static final int TAG_SYSTEM_NTP = 0xFFFFFF41;
- /** @hide */
- @SystemApi
- @TestApi
public static final int TAG_SYSTEM_PROBE = 0xFFFFFF42;
- /** @hide */
- public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF43;
- /** @hide */
- public static final int TAG_SYSTEM_GPS = 0xFFFFFF44;
- /** @hide */
- public static final int TAG_SYSTEM_PAC = 0xFFFFFF45;
- /** @hide */
- @SystemApi
- @TestApi
- public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFF46;
private static INetworkStatsService sStatsService;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ed5c1b1..dde1e6a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -1148,15 +1148,9 @@
final Context context = AppGlobals.getInitialApplication();
final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- final boolean hasLegacy = appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
+ return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
context.getApplicationInfo().uid,
- context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
-
- // STOPSHIP: only use app-op once permission model has fully landed
- final boolean requestedLegacy = !AppGlobals.getInitialApplication().getApplicationInfo()
- .isExternalStorageSandboxAllowed();
-
- return !(hasLegacy || requestedLegacy);
+ context.getOpPackageName()) != AppOpsManager.MODE_ALLOWED;
}
static File getDirectory(String variableName, String defaultPath) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 335ed21..488f417 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5802,6 +5802,14 @@
"autofill_field_classification";
/**
+ * Boolean indicating if the dark mode dialog shown on first toggle has been seen.
+ *
+ * @hide
+ */
+ public static final String DARK_MODE_DIALOG_SEEN =
+ "dark_mode_dialog_seen";
+
+ /**
* Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
*
* @hide
@@ -8849,6 +8857,7 @@
SILENCE_NOTIFICATION_GESTURE_COUNT,
SILENCE_CALL_GESTURE_COUNT,
SILENCE_TIMER_GESTURE_COUNT,
+ DARK_MODE_DIALOG_SEEN
};
/**
@@ -9029,6 +9038,7 @@
VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR);
+ VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
}
/**
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index b00eb8a..656127a 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -27,6 +27,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Rect;
+import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -67,9 +68,8 @@
private static final String TAG = AugmentedAutofillService.class.getSimpleName();
- // TODO(b/123100811): STOPSHIP use dynamic value, or change to false
- static final boolean DEBUG = true;
- static final boolean VERBOSE = false;
+ static boolean sDebug = Build.IS_USER ? false : true;
+ static boolean sVerbose = false;
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -87,9 +87,9 @@
private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() {
@Override
- public void onConnected() {
+ public void onConnected(boolean debug, boolean verbose) {
mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnConnected,
- AugmentedAutofillService.this));
+ AugmentedAutofillService.this, debug, verbose));
}
@Override
@@ -190,7 +190,12 @@
public void onDisconnected() {
}
- private void handleOnConnected() {
+ private void handleOnConnected(boolean debug, boolean verbose) {
+ if (sDebug || debug) {
+ Log.d(TAG, "handleOnConnected(): debug=" + debug + ", verbose=" + verbose);
+ }
+ sDebug = debug;
+ sVerbose = verbose;
onConnected();
}
@@ -215,7 +220,7 @@
mAutofillProxies.put(sessionId, proxy);
} else {
// TODO(b/123099468): figure out if it's ok to reuse the proxy; add logging
- if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ if (sDebug) Log.d(TAG, "Reusing proxy for session " + sessionId);
proxy.update(focusedId, focusedValue, callback);
}
@@ -248,11 +253,11 @@
private void handleOnUnbind() {
if (mAutofillProxies == null) {
- if (DEBUG) Log.d(TAG, "onUnbind(): no proxy to destroy");
+ if (sDebug) Log.d(TAG, "onUnbind(): no proxy to destroy");
return;
}
final int size = mAutofillProxies.size();
- if (DEBUG) Log.d(TAG, "onUnbind(): destroying " + size + " proxies");
+ if (sDebug) Log.d(TAG, "onUnbind(): destroying " + size + " proxies");
for (int i = 0; i < size; i++) {
final AutofillProxy proxy = mAutofillProxies.valueAt(i);
try {
@@ -373,7 +378,7 @@
return null;
}
if (rect == null) {
- if (DEBUG) Log.d(TAG, "getViewCoordinates(" + mFocusedId + ") returned null");
+ if (sDebug) Log.d(TAG, "getViewCoordinates(" + mFocusedId + ") returned null");
return null;
}
mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
@@ -410,7 +415,7 @@
public void requestShowFillUi(int width, int height, Rect anchorBounds,
IAutofillWindowPresenter presenter) throws RemoteException {
if (mCancellationSignal.isCanceled()) {
- if (VERBOSE) {
+ if (sVerbose) {
Log.v(TAG, "requestShowFillUi() not showing because request is cancelled");
}
return;
@@ -462,7 +467,7 @@
case REPORT_EVENT_ON_SUCCESS:
if (mFirstOnSuccessTime == 0) {
mFirstOnSuccessTime = SystemClock.elapsedRealtime();
- if (DEBUG) {
+ if (sDebug) {
Slog.d(TAG, "Service responded in " + TimeUtils.formatDuration(
mFirstOnSuccessTime - mFirstRequestTime));
}
@@ -476,7 +481,7 @@
case REPORT_EVENT_UI_SHOWN:
if (mUiFirstShownTime == 0) {
mUiFirstShownTime = SystemClock.elapsedRealtime();
- if (DEBUG) {
+ if (sDebug) {
Slog.d(TAG, "UI shown in " + TimeUtils.formatDuration(
mUiFirstShownTime - mFirstRequestTime));
}
@@ -485,7 +490,7 @@
case REPORT_EVENT_UI_DESTROYED:
if (mUiFirstDestroyedTime == 0) {
mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
- if (DEBUG) {
+ if (sDebug) {
Slog.d(TAG, "UI destroyed in " + TimeUtils.formatDuration(
mUiFirstDestroyedTime - mFirstRequestTime));
}
@@ -541,7 +546,7 @@
private void destroy() {
synchronized (mLock) {
if (mFillWindow != null) {
- if (DEBUG) Log.d(TAG, "destroying window");
+ if (sDebug) Log.d(TAG, "destroying window");
mFillWindow.destroy();
mFillWindow = null;
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index b989dd9..33e6a8c 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -15,7 +15,7 @@
*/
package android.service.autofill.augmented;
-import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,7 +48,7 @@
* could not provide autofill for the request.
*/
public void onSuccess(@Nullable FillResponse response) {
- if (DEBUG) Log.d(TAG, "onSuccess(): " + response);
+ if (sDebug) Log.d(TAG, "onSuccess(): " + response);
mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS);
if (response == null) return;
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 67f23d5..63ec2d8 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -15,7 +15,7 @@
*/
package android.service.autofill.augmented;
-import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.sDebug;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -56,7 +56,7 @@
public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
Preconditions.checkNotNull(values);
- if (DEBUG) {
+ if (sDebug) {
Log.d(TAG, "autofill() with " + values.size() + " values");
}
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index bd532a3..6a29d48 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -15,8 +15,8 @@
*/
package android.service.autofill.augmented;
-import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
-import static android.service.autofill.augmented.AugmentedAutofillService.VERBOSE;
+import static android.service.autofill.augmented.AugmentedAutofillService.sDebug;
+import static android.service.autofill.augmented.AugmentedAutofillService.sVerbose;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -100,7 +100,7 @@
* @throws IllegalArgumentException if the area is not compatible with this window
*/
public boolean update(@NonNull Area area, @NonNull View rootView, long flags) {
- if (DEBUG) {
+ if (sDebug) {
Log.d(TAG, "Updating " + area + " + with " + rootView);
}
// TODO(b/123100712): add test case for null
@@ -141,7 +141,7 @@
mFillView.setOnTouchListener(
(view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
- if (VERBOSE) Log.v(TAG, "Outside touch detected, hiding the window");
+ if (sVerbose) Log.v(TAG, "Outside touch detected, hiding the window");
hide();
}
return false;
@@ -149,7 +149,7 @@
);
mShowing = false;
mBounds = new Rect(area.getBounds());
- if (DEBUG) {
+ if (sDebug) {
Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
}
mUpdateCalled = true;
@@ -162,7 +162,7 @@
/** @hide */
void show() {
// TODO(b/123100712): check if updated first / throw exception
- if (DEBUG) Log.d(TAG, "show()");
+ if (sDebug) Log.d(TAG, "show()");
synchronized (mLock) {
checkNotDestroyedLocked();
if (mWm == null || mFillView == null) {
@@ -187,7 +187,7 @@
* <p>The window is not destroyed and can be shown again
*/
private void hide() {
- if (DEBUG) Log.d(TAG, "hide()");
+ if (sDebug) Log.d(TAG, "hide()");
synchronized (mLock) {
checkNotDestroyedLocked();
if (mWm == null || mFillView == null) {
@@ -204,7 +204,7 @@
}
private void handleShow(WindowManager.LayoutParams p) {
- if (DEBUG) Log.d(TAG, "handleShow()");
+ if (sDebug) Log.d(TAG, "handleShow()");
synchronized (mLock) {
if (mWm != null && mFillView != null) {
p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
@@ -219,7 +219,7 @@
}
private void handleHide() {
- if (DEBUG) Log.d(TAG, "handleHide()");
+ if (sDebug) Log.d(TAG, "handleHide()");
synchronized (mLock) {
if (mWm != null && mFillView != null && mShowing) {
mWm.removeView(mFillView);
@@ -234,7 +234,7 @@
* <p>Once destroyed, this window cannot be used anymore
*/
public void destroy() {
- if (DEBUG) {
+ if (sDebug) {
Log.d(TAG,
"destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView="
+ mFillView);
@@ -296,13 +296,13 @@
@Override
public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
boolean fitsSystemWindows, int layoutDirection) {
- if (DEBUG) Log.d(TAG, "FillWindowPresenter.show()");
+ if (sDebug) Log.d(TAG, "FillWindowPresenter.show()");
mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
}
@Override
public void hide(Rect transitionEpicenter) {
- if (DEBUG) Log.d(TAG, "FillWindowPresenter.hide()");
+ if (sDebug) Log.d(TAG, "FillWindowPresenter.hide()");
mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
}
}
diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
index 5096811..103fc4d 100644
--- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
+++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
@@ -31,7 +31,7 @@
* @hide
*/
oneway interface IAugmentedAutofillService {
- void onConnected();
+ void onConnected(boolean debug, boolean verbose);
void onDisconnected();
void onFillRequest(int sessionId, in IBinder autofillManagerClient, int taskId,
in ComponentName activityComponent, in AutofillId focusedId,
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index d8614a9..02ce873 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -86,11 +86,28 @@
* <code><{@link
* android.R.styleable#ContentCaptureService content-capture-service}></code> tag.
*
- * <p>This is a a sample XML file configuring a ContentCaptureService:
- * <pre> <content-capture-service
- * android:settingsActivity="foo.bar.SettingsActivity"
- * . . .
- * /></pre>
+ * <p>Here's an example of how to use it on {@code AndroidManifest.xml}:
+ *
+ * <pre>
+ * <service android:name=".MyContentCaptureService"
+ * android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.contentcapture.ContentCaptureService" />
+ * </intent-filter>
+ *
+ * <meta-data
+ * android:name="android.content_capture"
+ * android:resource="@xml/my_content_capture_service"/>
+ * </service>
+ * </pre>
+ *
+ * <p>And then on {@code res/xml/my_content_capture_service.xml}:
+ *
+ * <pre>
+ * <content-capture-service xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:settingsActivity="my.package.MySettingsActivity">
+ * </content-capture-service>
+ * </pre>
*/
public static final String SERVICE_META_DATA = "android.content_capture";
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index feff9db..f2aaead 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -63,7 +63,6 @@
DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false");
DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true");
DEFAULT_FLAGS.put(PIXEL_WALLPAPER_CATEGORY_SWITCH, "false");
- DEFAULT_FLAGS.put("settings_wifi_details_saved_screen", "true");
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
}
diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java
index 8eccc04..6fcdd71 100644
--- a/core/java/android/view/GestureExclusionTracker.java
+++ b/core/java/android/view/GestureExclusionTracker.java
@@ -20,6 +20,8 @@
import android.annotation.Nullable;
import android.graphics.Rect;
+import com.android.internal.util.Preconditions;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
@@ -31,6 +33,8 @@
*/
class GestureExclusionTracker {
private boolean mGestureExclusionViewsChanged = false;
+ private boolean mRootGestureExclusionRectsChanged = false;
+ private List<Rect> mRootGestureExclusionRects = Collections.emptyList();
private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>();
private List<Rect> mGestureExclusionRects = Collections.emptyList();
@@ -59,9 +63,9 @@
@Nullable
public List<Rect> computeChangedRects() {
- boolean changed = false;
+ boolean changed = mRootGestureExclusionRectsChanged;
final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
- final List<Rect> rects = new ArrayList<>();
+ final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects);
while (i.hasNext()) {
final GestureExclusionViewInfo info = i.next();
switch (info.update()) {
@@ -79,6 +83,7 @@
}
if (changed || mGestureExclusionViewsChanged) {
mGestureExclusionViewsChanged = false;
+ mRootGestureExclusionRectsChanged = false;
if (!mGestureExclusionRects.equals(rects)) {
mGestureExclusionRects = rects;
return rects;
@@ -87,6 +92,17 @@
return null;
}
+ public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
+ Preconditions.checkNotNull(rects, "rects must not be null");
+ mRootGestureExclusionRects = rects;
+ mRootGestureExclusionRectsChanged = true;
+ }
+
+ @NonNull
+ public List<Rect> getRootSystemGestureExclusionRects() {
+ return mRootGestureExclusionRects;
+ }
+
private static class GestureExclusionViewInfo {
public static final int CHANGED = 0;
public static final int UNCHANGED = 1;
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 2ba1e01..d415387 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -85,6 +85,7 @@
@Override
public void onWindowFocusLost() {
mHasWindowFocus = false;
+ getImm().unregisterImeConsumer(this);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c3a9465..096c988 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11648,14 +11648,11 @@
}
/**
- * When screen readers (one type of accessibility tool) decide what should be read to the
- * user, they typically look for input focusable ({@link #isFocusable()}) parents of
- * non-focusable text items, and read those focusable parents and their non-focusable children
- * as a unit. In some situations, this behavior is desirable for views that should not take
- * input focus. Setting an item to be screen reader focusable requests that the view be
- * treated as a unit by screen readers without any effect on input focusability. The default
- * value of {@code false} lets screen readers use other signals, like focusable, to determine
- * how to group items.
+ * Sets whether this View should be a focusable element for screen readers
+ * and include non-focusable Views from its subtree when providing feedback.
+ * <p>
+ * Note: this is similar to using <a href="#attr_android:focusable">{@code android:focusable},
+ * but does not impact input focus behavior.
*
* @param screenReaderFocusable Whether the view should be treated as a unit by screen reader
* accessibility tools.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5ca70ba..7ad118e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3829,6 +3829,24 @@
}
/**
+ * Set the root-level system gesture exclusion rects. These are added to those provided by
+ * the root's view hierarchy.
+ */
+ public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
+ mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects);
+ mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED);
+ }
+
+ /**
+ * Returns the root-level system gesture exclusion rects. These do not include those provided by
+ * the root's view hierarchy.
+ */
+ @NonNull
+ public List<Rect> getRootSystemGestureExclusionRects() {
+ return mGestureExclusionTracker.getRootSystemGestureExclusionRects();
+ }
+
+ /**
* Requests that the root render node is invalidated next time we perform a draw, such that
* {@link WindowCallbacks#onPostDraw} gets called.
*/
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index fc9d8c2..b0ec621 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -48,6 +48,7 @@
import android.transition.TransitionManager;
import android.view.accessibility.AccessibilityEvent;
+import java.util.Collections;
import java.util.List;
/**
@@ -2397,6 +2398,53 @@
return false;
}
+ /**
+ * Sets a list of areas within this window's coordinate space where the system should not
+ * intercept touch or other pointing device gestures.
+ *
+ * <p>This method should be used by apps that make use of
+ * {@link #takeSurface(SurfaceHolder.Callback2)} and do not have a view hierarchy available.
+ * Apps that do have a view hierarchy should use
+ * {@link View#setSystemGestureExclusionRects(List)} instead. This method does not modify or
+ * replace the gesture exclusion rects populated by individual views in this window's view
+ * hierarchy using {@link View#setSystemGestureExclusionRects(List)}.</p>
+ *
+ * <p>Use this to tell the system which specific sub-areas of a view need to receive gesture
+ * input in order to function correctly in the presence of global system gestures that may
+ * conflict. For example, if the system wishes to capture swipe-in-from-screen-edge gestures
+ * to provide system-level navigation functionality, a view such as a navigation drawer
+ * container can mark the left (or starting) edge of itself as requiring gesture capture
+ * priority using this API. The system may then choose to relax its own gesture recognition
+ * to allow the app to consume the user's gesture. It is not necessary for an app to register
+ * exclusion rects for broadly spanning regions such as the entirety of a
+ * <code>ScrollView</code> or for simple press and release click targets such as
+ * <code>Button</code>. Mark an exclusion rect when interacting with a view requires
+ * a precision touch gesture in a small area in either the X or Y dimension, such as
+ * an edge swipe or dragging a <code>SeekBar</code> thumb.</p>
+ *
+ * <p>Do not modify the provided list after this method is called.</p>
+ *
+ * @param rects A list of precision gesture regions that this window needs to function correctly
+ */
+ @SuppressWarnings("unused")
+ public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) {
+ throw new UnsupportedOperationException("window does not support gesture exclusion rects");
+ }
+
+ /**
+ * Retrieve the list of areas within this window's coordinate space where the system should not
+ * intercept touch or other pointing device gestures. This is the list as set by
+ * {@link #setSystemGestureExclusionRects(List)} or an empty list if
+ * {@link #setSystemGestureExclusionRects(List)} has not been called. It does not include
+ * exclusion rects set by this window's view hierarchy.
+ *
+ * @return a list of system gesture exclusion rects specific to this window
+ */
+ @NonNull
+ public List<Rect> getSystemGestureExclusionRects() {
+ return Collections.emptyList();
+ }
+
/** @hide */
public void setTheme(int resId) {
}
diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java
index 3de1a03..a07d46d 100644
--- a/core/java/android/view/autofill/AutofillManagerInternal.java
+++ b/core/java/android/view/autofill/AutofillManagerInternal.java
@@ -45,4 +45,12 @@
@Nullable
public abstract AutofillOptions getAutofillOptions(@NonNull String packageName,
long versionCode, @UserIdInt int userId);
+
+ /**
+ * Checks whether the given {@code uid} owns the
+ * {@link android.service.autofill.augmented.AugmentedAutofillService} implementation associated
+ * with the given {@code userId}.
+ */
+ public abstract boolean isAugmentedAutofillServiceForUser(@NonNull int callingUid,
+ @UserIdInt int userId);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 6f9d4d3..54ebf55 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -35,7 +35,8 @@
public final class ContentCaptureCondition implements Parcelable {
/**
- * When set, package should use the {@link LocusId#getId()} as a regular expression.
+ * When set, package should use the {@link LocusId#getId()} as a regular expression (using the
+ * {@link java.util.regex.Pattern} format).
*/
public static final int FLAG_IS_REGEX = 0x2;
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 7a6e2ad..26454c0 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -343,15 +343,6 @@
private MainContentCaptureSession mMainSession;
/** @hide */
- public interface ContentCaptureClient {
- /**
- * Gets the component name of the client.
- */
- @NonNull
- ComponentName contentCaptureClientGetComponentName();
- }
-
- /** @hide */
public ContentCaptureManager(@NonNull Context context,
@NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) {
mContext = Preconditions.checkNotNull(context, "context cannot be null");
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5e00425..fd73856 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1951,6 +1951,23 @@
}
/**
+ * Unregister for IME state callbacks and applying visibility in
+ * {@link android.view.ImeInsetsSourceConsumer}.
+ * @hide
+ */
+ public void unregisterImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) {
+ if (imeInsetsConsumer == null) {
+ throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null.");
+ }
+
+ synchronized (mH) {
+ if (mImeInsetsConsumer == imeInsetsConsumer) {
+ mImeInsetsConsumer = null;
+ }
+ }
+ }
+
+ /**
* Call showSoftInput with currently focused view.
* @return {@code true} if IME can be shown.
* @hide
diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java
index 7b23674..11e0e2c 100644
--- a/core/java/android/view/textclassifier/ExtrasUtils.java
+++ b/core/java/android/view/textclassifier/ExtrasUtils.java
@@ -36,6 +36,7 @@
// TODO: Make this a TestApi for CTS testing.
public final class ExtrasUtils {
+ // Keys for response objects.
private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data";
private static final String ENTITIES_EXTRAS = "entities-extras";
private static final String ACTION_INTENT = "action-intent";
@@ -48,6 +49,10 @@
private static final String TEXT_LANGUAGES = "text-languages";
private static final String ENTITIES = "entities";
+ // Keys for request objects.
+ private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED =
+ "is-serialized-entity-data-enabled";
+
private ExtrasUtils() {}
/**
@@ -308,7 +313,23 @@
/**
* Returns a list of entities contained in the {@code extra}.
*/
+ @Nullable
public static List<Bundle> getEntities(Bundle container) {
return container.getParcelableArrayList(ENTITIES);
}
+
+ /**
+ * Whether the annotator should populate serialized entity data into the result object.
+ */
+ public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) {
+ return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED);
+ }
+
+ /**
+ * To indicate whether the annotator should populate serialized entity data in the result
+ * object.
+ */
+ public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) {
+ bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled);
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index 236f89b..d3d61a7 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.icu.util.ULocale;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,54 +31,65 @@
import java.util.Arrays;
/**
- * A text classifier event.
+ * This class represents events that are sent by components to the {@link TextClassifier} to report
+ * something of note that relates to a feature powered by the TextClassifier. The TextClassifier may
+ * log these events or use them to improve future responses to queries.
+ * <p>
+ * Each categories of the events have their own subclass. Events of each types has an associated
+ * set of related properties. You can find the specification of them in the subclasses.
*/
-// TODO: Comprehensive javadoc.
-public final class TextClassifierEvent implements Parcelable {
+public abstract class TextClassifierEvent implements Parcelable {
- public static final @android.annotation.NonNull 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];
- }
- };
+ private static final int PARCEL_TOKEN_TEXT_SELECTION_EVENT = 1;
+ private static final int PARCEL_TOKEN_TEXT_LINKIFY_EVENT = 2;
+ private static final int PARCEL_TOKEN_CONVERSATION_ACTION_EVENT = 3;
+ private static final int PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT = 4;
/** @hide **/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({CATEGORY_UNDEFINED, CATEGORY_SELECTION, CATEGORY_LINKIFY,
+ @IntDef({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 */
+
+ /**
+ * Smart selection
+ *
+ * @see TextSelectionEvent
+ */
public static final int CATEGORY_SELECTION = 1;
- /** Linkify */
+ /**
+ * Linkify
+ *
+ * @see TextLinkifyEvent
+ */
public static final int CATEGORY_LINKIFY = 2;
- /** Conversation actions */
+ /**
+ * Conversation actions
+ *
+ * @see ConversationActionsEvent
+ */
public static final int CATEGORY_CONVERSATION_ACTIONS = 3;
- /** Language detection */
+ /**
+ * Language detection
+ *
+ * @see LanguageDetectionEvent
+ */
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, TYPE_ACTIONS_GENERATED})
+ @IntDef({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, TYPE_ACTIONS_GENERATED})
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. */
@@ -119,63 +131,49 @@
/** TextClassifier generated some actions */
public static final int TYPE_ACTIONS_GENERATED = 20;
- @Category private final int mEventCategory;
- @Type private final int mEventType;
- @Nullable private final String[] mEntityTypes;
- @Nullable private final TextClassificationContext mEventContext;
- @Nullable private final String mResultId;
+ @Category
+ private final int mEventCategory;
+ @Type
+ private final int mEventType;
+ @Nullable
+ private final String[] mEntityTypes;
+ @Nullable
+ private final TextClassificationContext mEventContext;
+ @Nullable
+ private final String mResultId;
private final int mEventIndex;
- private final long mEventTime;
+ private final float[] mScores;
+ @Nullable
+ private final String mModelName;
+ private final int[] mActionIndices;
private final Bundle mExtras;
- // Smart selection.
- private final int mRelativeWordStartIndex;
- private final int mRelativeWordEndIndex;
- private final int mRelativeSuggestedWordStartIndex;
- private final int mRelativeSuggestedWordEndIndex;
+ private TextClassifierEvent(Builder builder) {
+ mEventCategory = builder.mEventCategory;
+ mEventType = builder.mEventType;
+ mEntityTypes = builder.mEntityTypes;
+ mEventContext = builder.mEventContext;
+ mResultId = builder.mResultId;
+ mEventIndex = builder.mEventIndex;
+ mScores = builder.mScores;
+ mModelName = builder.mModelName;
+ mActionIndices = builder.mActionIndices;
+ mExtras = builder.mExtras == null ? Bundle.EMPTY : builder.mExtras;
+ }
- // Smart action.
- private final int[] mActionIndices;
-
- // Language detection.
- @Nullable private final String mLanguage;
- private final float mScore;
-
- @Nullable private final String mModelName;
-
- private TextClassifierEvent(
- int eventCategory,
- int eventType,
- String[] entityTypes,
- TextClassificationContext eventContext,
- String resultId,
- int eventIndex,
- long eventTime,
- Bundle extras,
- int relativeWordStartIndex,
- int relativeWordEndIndex,
- int relativeSuggestedWordStartIndex,
- int relativeSuggestedWordEndIndex,
- int[] actionIndex,
- String language,
- float score,
- String modelVersion) {
- mEventCategory = eventCategory;
- mEventType = eventType;
- mEntityTypes = entityTypes;
- mEventContext = eventContext;
- mResultId = resultId;
- mEventIndex = eventIndex;
- mEventTime = eventTime;
- mExtras = extras;
- mRelativeWordStartIndex = relativeWordStartIndex;
- mRelativeWordEndIndex = relativeWordEndIndex;
- mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex;
- mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
- mActionIndices = actionIndex;
- mLanguage = language;
- mScore = score;
- mModelName = modelVersion;
+ private TextClassifierEvent(Parcel in) {
+ mEventCategory = in.readInt();
+ mEventType = in.readInt();
+ mEntityTypes = in.readStringArray();
+ mEventContext = in.readParcelable(null);
+ mResultId = in.readString();
+ mEventIndex = in.readInt();
+ int scoresLength = in.readInt();
+ mScores = new float[scoresLength];
+ in.readFloatArray(mScores);
+ mModelName = in.readString();
+ mActionIndices = in.createIntArray();
+ mExtras = in.readBundle();
}
@Override
@@ -183,44 +181,62 @@
return 0;
}
+ @NonNull
+ public static final Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() {
+ @Override
+ public TextClassifierEvent createFromParcel(Parcel in) {
+ int token = in.readInt();
+ if (token == PARCEL_TOKEN_TEXT_SELECTION_EVENT) {
+ return new TextSelectionEvent(in);
+ }
+ if (token == PARCEL_TOKEN_TEXT_LINKIFY_EVENT) {
+ return new TextLinkifyEvent(in);
+ }
+ if (token == PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT) {
+ return new LanguageDetectionEvent(in);
+ }
+ if (token == PARCEL_TOKEN_CONVERSATION_ACTION_EVENT) {
+ return new ConversationActionsEvent(in);
+ }
+ throw new IllegalStateException("Unexpected input event type token in parcel.");
+ }
+
+ @Override
+ public TextClassifierEvent[] newArray(int size) {
+ return new TextClassifierEvent[size];
+ }
+ };
+
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getParcelToken());
dest.writeInt(mEventCategory);
dest.writeInt(mEventType);
dest.writeStringArray(mEntityTypes);
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);
- dest.writeFloat(mScore);
+ dest.writeInt(mScores.length);
+ dest.writeFloatArray(mScores);
dest.writeString(mModelName);
+ dest.writeIntArray(mActionIndices);
+ dest.writeBundle(mExtras);
}
- private static TextClassifierEvent readFromParcel(Parcel in) {
- return new TextClassifierEvent(
- /* eventCategory= */ in.readInt(),
- /* eventType= */ in.readInt(),
- /* entityTypes=*/ in.readStringArray(),
- /* 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(),
- /* score= */ in.readFloat(),
- /* modelVersion= */ in.readString());
+ private int getParcelToken() {
+ if (this instanceof TextSelectionEvent) {
+ return PARCEL_TOKEN_TEXT_SELECTION_EVENT;
+ }
+ if (this instanceof TextLinkifyEvent) {
+ return PARCEL_TOKEN_TEXT_LINKIFY_EVENT;
+ }
+ if (this instanceof LanguageDetectionEvent) {
+ return PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT;
+ }
+ if (this instanceof ConversationActionsEvent) {
+ return PARCEL_TOKEN_CONVERSATION_ACTION_EVENT;
+ }
+ throw new IllegalArgumentException("Unexpected type: " + this.getClass().getSimpleName());
}
/**
@@ -241,6 +257,8 @@
/**
* Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ *
+ * @see Builder#setEntityTypes(String...) for supported types.
*/
@NonNull
public String[] getEntityTypes() {
@@ -270,13 +288,33 @@
return mEventIndex;
}
- // TODO: Remove this API.
/**
- * Returns the time this event occurred. This is the number of milliseconds since
- * January 1, 1970, 00:00:00 GMT. 0 indicates not set.
+ * Returns the scores of the suggestions.
*/
- public long getEventTime() {
- return mEventTime;
+ @NonNull
+ public float[] getScores() {
+ return mScores;
+ }
+
+ /**
+ * Returns the model name.
+ */
+ @Nullable
+ public String getModelName() {
+ return mModelName;
+ }
+
+ /**
+ * 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.
+ *
+ * @see Builder#setActionIndices(int...)
+ */
+ @NonNull
+ public int[] getActionIndices() {
+ return mActionIndices;
}
/**
@@ -289,151 +327,149 @@
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;
- }
-
- /**
- * Returns the score of the suggestion.
- */
- public float getScore() {
- return mScore;
- }
-
- /**
- * Returns the model name.
- * @hide
- */
- @Nullable
- public String getModelName() {
- return mModelName;
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(128);
+ out.append(this.getClass().getSimpleName());
+ out.append("{");
+ out.append("mEventCategory=").append(mEventCategory);
+ out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes));
+ out.append(", mEventContext=").append(mEventContext);
+ out.append(", mResultId=").append(mResultId);
+ out.append(", mEventIndex=").append(mEventIndex);
+ out.append(", mExtras=").append(mExtras);
+ out.append(", mScores=").append(Arrays.toString(mScores));
+ out.append(", mModelName=").append(mModelName);
+ out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
+ out.append("}");
+ return out.toString();
}
/**
* Builder to build a text classifier event.
+ *
+ * @param <T> The subclass to be built.
*/
- public static final class Builder {
+ public abstract static class Builder<T extends Builder<T>> {
private final int mEventCategory;
private final int mEventType;
private String[] mEntityTypes = new String[0];
- @Nullable private TextClassificationContext mEventContext;
- @Nullable private String mResultId;
+ @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;
- private float mScore;
-
+ private float[] mScores = new float[0];
+ @Nullable
private String mModelName;
+ private int[] mActionIndices = new int[0];
+ @Nullable
+ private Bundle mExtras;
/**
* 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}
+ * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED}
*/
- public Builder(@Category int eventCategory, @Type int eventType) {
+ private Builder(@Category int eventCategory, @Type int eventType) {
mEventCategory = eventCategory;
mEventType = eventType;
}
/**
* Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ * <p>
+ * Supported types:
+ * <p>See {@link TextClassifier.EntityType}
+ * <p>See {@link ConversationAction.ActionType}
+ * <p>See {@link ULocale#toLanguageTag()}
*/
@NonNull
- public Builder setEntityTypes(@NonNull String... entityTypes) {
+ public T setEntityTypes(@NonNull String... entityTypes) {
+ Preconditions.checkNotNull(entityTypes);
mEntityTypes = new String[entityTypes.length];
System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length);
- return this;
+ return self();
}
/**
* Sets the event context.
*/
@NonNull
- public Builder setEventContext(@Nullable TextClassificationContext eventContext) {
+ public T setEventContext(@Nullable TextClassificationContext eventContext) {
mEventContext = eventContext;
- return this;
+ return self();
}
/**
* Sets the id of the text classifier result related to this event.
*/
@NonNull
- public Builder setResultId(@Nullable String resultId) {
+ public T setResultId(@Nullable String resultId) {
mResultId = resultId;
- return this;
+ return self();
}
/**
- * Sets the index of this events in the series of events it belongs to.
+ * Sets the index of this event in the series of events it belongs to.
*/
@NonNull
- public Builder setEventIndex(int eventIndex) {
+ public T setEventIndex(int eventIndex) {
mEventIndex = eventIndex;
- return this;
+ return self();
}
- // TODO: Remove this API.
/**
- * Sets the time this event occurred. This is the number of milliseconds since
- * January 1, 1970, 00:00:00 GMT. 0 indicates not set.
+ * Sets the scores of the suggestions.
*/
@NonNull
- public Builder setEventTime(long eventTime) {
- mEventTime = eventTime;
- return this;
+ public T setScores(@NonNull float... scores) {
+ Preconditions.checkNotNull(scores);
+ mScores = new float[scores.length];
+ System.arraycopy(scores, 0, mScores, 0, scores.length);
+ return self();
+ }
+
+ /**
+ * Sets the model name string.
+ */
+ @NonNull
+ public T setModelName(@Nullable String modelVersion) {
+ mModelName = modelVersion;
+ return self();
+ }
+
+ /**
+ * 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.
+ * These indices give an indication of the position of the actions that are being reported.
+ * <p>
+ * E.g.
+ * <pre>
+ * // 3 smart actions are shown at index 0, 1, 2 respectively in response to a link click.
+ * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_ACTIONS_SHOWN)
+ * .setEventIndex(0, 1, 2)
+ * ...
+ * .build();
+ *
+ * ...
+ *
+ * // Smart action at index 1 is activated.
+ * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_SMART_ACTION)
+ * .setEventIndex(1)
+ * ...
+ * .build();
+ * </pre>
+ *
+ * @see TextClassification#getActions()
+ */
+ @NonNull
+ public T setActionIndices(@NonNull int... actionIndices) {
+ mActionIndices = new int[actionIndices.length];
+ System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length);
+ return self();
}
/**
@@ -445,136 +481,545 @@
* objects in this bundle.
*/
@NonNull
- public Builder setExtras(@NonNull Bundle extras) {
+ public T setExtras(@NonNull Bundle extras) {
mExtras = Preconditions.checkNotNull(extras);
- return this;
+ return self();
}
- /**
- * 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;
- }
-
- /**
- * Sets the score of the suggestion.
- */
- @NonNull
- public Builder setScore(float score) {
- mScore = score;
- return this;
- }
-
- /**
- * Sets the model name string.
- * @hide
- */
- public Builder setModelName(@Nullable String modelVersion) {
- mModelName = modelVersion;
- return this;
- }
-
- /**
- * Builds and returns a text classifier event.
- */
- @NonNull
- public TextClassifierEvent build() {
- mExtras = mExtras == null ? Bundle.EMPTY : mExtras;
- return new TextClassifierEvent(
- mEventCategory,
- mEventType,
- mEntityTypes,
- mEventContext,
- mResultId,
- mEventIndex,
- mEventTime,
- mExtras,
- mRelativeWordStartIndex,
- mRelativeWordEndIndex,
- mRelativeSuggestedWordStartIndex,
- mRelativeSuggestedWordEndIndex,
- mActionIndices,
- mLanguage,
- mScore,
- mModelName);
- }
- // TODO: Add build(boolean validate).
+ abstract T self();
}
- @Override
- public String toString() {
- StringBuilder out = new StringBuilder(128);
- out.append("TextClassifierEvent{");
- out.append("mEventCategory=").append(mEventCategory);
- out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes));
- out.append(", mEventContext=").append(mEventContext);
- out.append(", mResultId=").append(mResultId);
- out.append(", mEventIndex=").append(mEventIndex);
- out.append(", mEventTime=").append(mEventTime);
- out.append(", mExtras=").append(mExtras);
- out.append(", mRelativeWordStartIndex=").append(mRelativeWordStartIndex);
- out.append(", mRelativeWordEndIndex=").append(mRelativeWordEndIndex);
- out.append(", mRelativeSuggestedWordStartIndex=").append(mRelativeSuggestedWordStartIndex);
- out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex);
- out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
- out.append(", mLanguage=").append(mLanguage);
- out.append(", mScore=").append(mScore);
- out.append(", mModelName=").append(mModelName);
- out.append("}");
- return out.toString();
+ /**
+ * This class represents events that are related to the smart text selection feature.
+ * <p>
+ * <pre>
+ * // User started a selection. e.g. "York" in text "New York City, NY".
+ * new TextSelectionEvent.Builder(TYPE_SELECTION_STARTED)
+ * .setEventContext(classificationContext)
+ * .setEventIndex(0)
+ * .build();
+ *
+ * // System smart-selects a recognized entity. e.g. "New York City".
+ * new TextSelectionEvent.Builder(TYPE_SMART_SELECTION_MULTI)
+ * .setEventContext(classificationContext)
+ * .setResultId(textSelection.getId())
+ * .setRelativeWordStartIndex(-1) // Goes back one word to "New" from "York".
+ * .setRelativeWordEndIndex(2) // Goes forward 2 words from "York" to start of ",".
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setEventIndex(1)
+ * .build();
+ *
+ * // User resets the selection to the original selection. i.e. "York".
+ * new TextSelectionEvent.Builder(TYPE_SELECTION_RESET)
+ * .setEventContext(classificationContext)
+ * .setResultId(textSelection.getId())
+ * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above.
+ * .setRelativeSuggestedWordEndIndex(2) // Repeated from above.
+ * .setRelativeWordStartIndex(0) // Original selection is always at (0, 1].
+ * .setRelativeWordEndIndex(1)
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setEventIndex(2)
+ * .build();
+ *
+ * // User modified the selection. e.g. "New".
+ * new TextSelectionEvent.Builder(TYPE_SELECTION_MODIFIED)
+ * .setEventContext(classificationContext)
+ * .setResultId(textSelection.getId())
+ * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above.
+ * .setRelativeSuggestedWordEndIndex(2) // Repeated from above.
+ * .setRelativeWordStartIndex(-1) // Goes backward one word from "York" to
+ * "New".
+ * .setRelativeWordEndIndex(0) // Goes backward one word to exclude "York".
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setEventIndex(3)
+ * .build();
+ *
+ * // Smart (contextual) actions (at indices, 0, 1, 2) presented to the user.
+ * // e.g. "Map", "Ride share", "Explore".
+ * new TextSelectionEvent.Builder(TYPE_ACTIONS_SHOWN)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setActionIndices(0, 1, 2)
+ * .setEventIndex(4)
+ * .build();
+ *
+ * // User chooses the "Copy" action.
+ * new TextSelectionEvent.Builder(TYPE_COPY_ACTION)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setEventIndex(5)
+ * .build();
+ *
+ * // User chooses smart action at index 1. i.e. "Ride share".
+ * new TextSelectionEvent.Builder(TYPE_SMART_ACTION)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setActionIndices(1)
+ * .setEventIndex(5)
+ * .build();
+ *
+ * // Selection dismissed.
+ * new TextSelectionEvent.Builder(TYPE_SELECTION_DESTROYED)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setEventIndex(6)
+ * .build();
+ * </pre>
+ * <p>
+ */
+ public static final class TextSelectionEvent extends TextClassifierEvent implements Parcelable {
+
+ @NonNull
+ public static final Creator<TextSelectionEvent> CREATOR =
+ new Creator<TextSelectionEvent>() {
+ @Override
+ public TextSelectionEvent createFromParcel(Parcel in) {
+ in.readInt(); // skip token, we already know this is a TextSelectionEvent
+ return new TextSelectionEvent(in);
+ }
+
+ @Override
+ public TextSelectionEvent[] newArray(int size) {
+ return new TextSelectionEvent[size];
+ }
+ };
+
+ final int mRelativeWordStartIndex;
+ final int mRelativeWordEndIndex;
+ final int mRelativeSuggestedWordStartIndex;
+ final int mRelativeSuggestedWordEndIndex;
+
+ private TextSelectionEvent(TextSelectionEvent.Builder builder) {
+ super(builder);
+ mRelativeWordStartIndex = builder.mRelativeWordStartIndex;
+ mRelativeWordEndIndex = builder.mRelativeWordEndIndex;
+ mRelativeSuggestedWordStartIndex = builder.mRelativeSuggestedWordStartIndex;
+ mRelativeSuggestedWordEndIndex = builder.mRelativeSuggestedWordEndIndex;
+ }
+
+ private TextSelectionEvent(Parcel in) {
+ super(in);
+ mRelativeWordStartIndex = in.readInt();
+ mRelativeWordEndIndex = in.readInt();
+ mRelativeSuggestedWordStartIndex = in.readInt();
+ mRelativeSuggestedWordEndIndex = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mRelativeWordStartIndex);
+ dest.writeInt(mRelativeWordEndIndex);
+ dest.writeInt(mRelativeSuggestedWordStartIndex);
+ dest.writeInt(mRelativeSuggestedWordEndIndex);
+ }
+
+ /**
+ * Returns the relative word index of the start of the selection.
+ */
+ public int getRelativeWordStartIndex() {
+ return mRelativeWordStartIndex;
+ }
+
+ /**
+ * Returns the relative word (exclusive) index of the end of the selection.
+ */
+ public int getRelativeWordEndIndex() {
+ return mRelativeWordEndIndex;
+ }
+
+ /**
+ * Returns the relative word index of the start of the smart selection.
+ */
+ public int getRelativeSuggestedWordStartIndex() {
+ return mRelativeSuggestedWordStartIndex;
+ }
+
+ /**
+ * Returns the relative word (exclusive) index of the end of the
+ * smart selection.
+ */
+ public int getRelativeSuggestedWordEndIndex() {
+ return mRelativeSuggestedWordEndIndex;
+ }
+
+ /**
+ * Builder class for {@link TextSelectionEvent}.
+ */
+ public static final class Builder extends
+ TextClassifierEvent.Builder<TextSelectionEvent.Builder> {
+ int mRelativeWordStartIndex;
+ int mRelativeWordEndIndex;
+ int mRelativeSuggestedWordStartIndex;
+ int mRelativeSuggestedWordEndIndex;
+
+ /**
+ * Creates a builder for building {@link TextSelectionEvent}s.
+ *
+ * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED}
+ */
+ public Builder(@Type int eventType) {
+ super(CATEGORY_SELECTION, eventType);
+ }
+
+ /**
+ * Sets the relative word index of the start of the selection.
+ */
+ @NonNull
+ public Builder setRelativeWordStartIndex(int relativeWordStartIndex) {
+ mRelativeWordStartIndex = relativeWordStartIndex;
+ return this;
+ }
+
+ /**
+ * Sets the relative word (exclusive) index of the end of the
+ * selection.
+ */
+ @NonNull
+ public Builder setRelativeWordEndIndex(int relativeWordEndIndex) {
+ mRelativeWordEndIndex = relativeWordEndIndex;
+ return this;
+ }
+
+ /**
+ * Sets the relative word index of the start of the smart
+ * selection.
+ */
+ @NonNull
+ public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) {
+ mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex;
+ return this;
+ }
+
+ /**
+ * Sets the relative word (exclusive) index of the end of the
+ * smart selection.
+ */
+ @NonNull
+ public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) {
+ mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
+ return this;
+ }
+
+ @Override
+ TextSelectionEvent.Builder self() {
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link TextSelectionEvent}.
+ */
+ @NonNull
+ public TextSelectionEvent build() {
+ return new TextSelectionEvent(this);
+ }
+ }
+ }
+
+ /**
+ * This class represents events that are related to the smart linkify feature.
+ * <p>
+ * <pre>
+ * // User clicked on a link.
+ * new TextLinkifyEvent.Builder(TYPE_LINK_CLICKED)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setEventIndex(0)
+ * .build();
+ *
+ * // Smart (contextual) actions presented to the user in response to a link click.
+ * new TextLinkifyEvent.Builder(TYPE_ACTIONS_SHOWN)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setActionIndices(range(textClassification.getActions().size()))
+ * .setEventIndex(1)
+ * .build();
+ *
+ * // User chooses smart action at index 0.
+ * new TextLinkifyEvent.Builder(TYPE_SMART_ACTION)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(textClassification.getEntity(0))
+ * .setScore(textClassification.getConfidenceScore(entityType))
+ * .setActionIndices(0)
+ * .setEventIndex(2)
+ * .build();
+ * </pre>
+ */
+ public static final class TextLinkifyEvent extends TextClassifierEvent implements Parcelable {
+
+ @NonNull
+ public static final Creator<TextLinkifyEvent> CREATOR =
+ new Creator<TextLinkifyEvent>() {
+ @Override
+ public TextLinkifyEvent createFromParcel(Parcel in) {
+ in.readInt(); // skip token, we already know this is a TextLinkifyEvent
+ return new TextLinkifyEvent(in);
+ }
+
+ @Override
+ public TextLinkifyEvent[] newArray(int size) {
+ return new TextLinkifyEvent[size];
+ }
+ };
+
+ private TextLinkifyEvent(Parcel in) {
+ super(in);
+ }
+
+ private TextLinkifyEvent(TextLinkifyEvent.Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Builder class for {@link TextLinkifyEvent}.
+ */
+ public static final class Builder
+ extends TextClassifierEvent.Builder<TextLinkifyEvent.Builder> {
+ /**
+ * Creates a builder for building {@link TextLinkifyEvent}s.
+ *
+ * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION}
+ */
+ public Builder(@Type int eventType) {
+ super(TextClassifierEvent.CATEGORY_LINKIFY, eventType);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link TextLinkifyEvent}.
+ */
+ @NonNull
+ public TextLinkifyEvent build() {
+ return new TextLinkifyEvent(this);
+ }
+ }
+ }
+
+ /**
+ * This class represents events that are related to the language detection feature.
+ * <p>
+ * <pre>
+ * // Translate action shown for foreign text.
+ * new LanguageDetectionEvent.Builder(TYPE_ACTIONS_SHOWN)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(language)
+ * .setScore(score)
+ * .setActionIndices(textClassification.getActions().indexOf(translateAction))
+ * .setEventIndex(0)
+ * .build();
+ *
+ * // Translate action selected.
+ * new LanguageDetectionEvent.Builder(TYPE_SMART_ACTION)
+ * .setEventContext(classificationContext)
+ * .setResultId(textClassification.getId())
+ * .setEntityTypes(language)
+ * .setScore(score)
+ * .setActionIndices(textClassification.getActions().indexOf(translateAction))
+ * .setEventIndex(1)
+ * .build();
+ */
+ public static final class LanguageDetectionEvent extends TextClassifierEvent
+ implements Parcelable {
+
+ @NonNull
+ public static final Creator<LanguageDetectionEvent> CREATOR =
+ new Creator<LanguageDetectionEvent>() {
+ @Override
+ public LanguageDetectionEvent createFromParcel(Parcel in) {
+ // skip token, we already know this is a LanguageDetectionEvent.
+ in.readInt();
+ return new LanguageDetectionEvent(in);
+ }
+
+ @Override
+ public LanguageDetectionEvent[] newArray(int size) {
+ return new LanguageDetectionEvent[size];
+ }
+ };
+
+ @Nullable
+ private final ULocale mLocale;
+
+ private LanguageDetectionEvent(Parcel in) {
+ super(in);
+ final String languageTag = in.readString();
+ mLocale = languageTag == null ? null : ULocale.forLanguageTag(languageTag);
+ }
+
+ private LanguageDetectionEvent(LanguageDetectionEvent.Builder builder) {
+ super(builder);
+ mLocale = builder.mLocale;
+ }
+
+ /**
+ * Returns the detected locale.
+ */
+ @Nullable
+ public ULocale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * Builder class for {@link LanguageDetectionEvent}.
+ */
+ public static final class Builder
+ extends TextClassifierEvent.Builder<LanguageDetectionEvent.Builder> {
+ @Nullable
+ private ULocale mLocale;
+
+ /**
+ * Creates a builder for building {@link TextSelectionEvent}s.
+ *
+ * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION}
+ */
+ public Builder(@Type int eventType) {
+ super(TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType);
+ }
+
+ /**
+ * Sets the detected locale.
+ */
+ @NonNull
+ public Builder setLocale(@Nullable ULocale locale) {
+ mLocale = locale;
+ return this;
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link LanguageDetectionEvent}.
+ */
+ @NonNull
+ public LanguageDetectionEvent build() {
+ return new LanguageDetectionEvent(this);
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
+ }
+ }
+
+ /**
+ * This class represents events that are related to the conversation actions feature.
+ * <p>
+ * <pre>
+ * // Conversation (contextual) actions/replies generated.
+ * new ConversationActionsEvent.Builder(TYPE_ACTIONS_GENERATED)
+ * .setEventContext(classificationContext)
+ * .setResultId(conversationActions.getId())
+ * .setEntityTypes(getTypes(conversationActions))
+ * .setActionIndices(range(conversationActions.getActions().size()))
+ * .setEventIndex(0)
+ * .build();
+ *
+ * // Conversation actions/replies presented to user.
+ * new ConversationActionsEvent.Builder(TYPE_ACTIONS_SHOWN)
+ * .setEventContext(classificationContext)
+ * .setResultId(conversationActions.getId())
+ * .setEntityTypes(getTypes(conversationActions))
+ * .setActionIndices(range(conversationActions.getActions().size()))
+ * .setEventIndex(1)
+ * .build();
+ *
+ * // User clicked the "Reply" button to compose their custom reply.
+ * new ConversationActionsEvent.Builder(TYPE_MANUAL_REPLY)
+ * .setEventContext(classificationContext)
+ * .setResultId(conversationActions.getId())
+ * .setEventIndex(2)
+ * .build();
+ *
+ * // User selected a smart (contextual) action/reply.
+ * new ConversationActionsEvent.Builder(TYPE_SMART_ACTION)
+ * .setEventContext(classificationContext)
+ * .setResultId(conversationActions.getId())
+ * .setEntityTypes(conversationActions.get(1).getType())
+ * .setScore(conversationAction.get(1).getConfidenceScore())
+ * .setActionIndices(1)
+ * .setEventIndex(2)
+ * .build();
+ * </pre>
+ */
+ public static final class ConversationActionsEvent extends TextClassifierEvent
+ implements Parcelable {
+
+ @NonNull
+ public static final Creator<ConversationActionsEvent> CREATOR =
+ new Creator<ConversationActionsEvent>() {
+ @Override
+ public ConversationActionsEvent createFromParcel(Parcel in) {
+ // skip token, we already know this is a ConversationActionsEvent.
+ in.readInt();
+ return new ConversationActionsEvent(in);
+ }
+
+ @Override
+ public ConversationActionsEvent[] newArray(int size) {
+ return new ConversationActionsEvent[size];
+ }
+ };
+
+ private ConversationActionsEvent(Parcel in) {
+ super(in);
+ }
+
+ private ConversationActionsEvent(ConversationActionsEvent.Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Builder class for {@link ConversationActionsEvent}.
+ */
+ public static final class Builder
+ extends TextClassifierEvent.Builder<ConversationActionsEvent.Builder> {
+ /**
+ * Creates a builder for building {@link TextSelectionEvent}s.
+ *
+ * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION}
+ */
+ public Builder(@Type int eventType) {
+ super(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link ConversationActionsEvent}.
+ */
+ @NonNull
+ public ConversationActionsEvent build() {
+ return new ConversationActionsEvent(this);
+ }
+ }
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
index 6a12250..3e088b8 100644
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -65,9 +65,10 @@
final LogMaker log = new LogMaker(category)
.setSubtype(getLogType(event))
.addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
- .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event))
- .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore());
-
+ .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event));
+ if (event.getScores().length >= 1) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]);
+ }
String[] entityTypes = event.getEntityTypes();
// The old logger does not support a field of list type, and thus workaround by store them
// in three separate fields. This is not an issue with the new logger.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 323bf59..3297523 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -307,6 +307,8 @@
final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
final AnnotatorModel annotatorImpl =
getAnnotatorImpl(request.getDefaultLocales());
+ final boolean isSerializedEntityDataEnabled =
+ ExtrasUtils.isSerializedEntityDataEnabled(request);
final AnnotatorModel.AnnotatedSpan[] annotations =
annotatorImpl.annotate(
textString,
@@ -314,7 +316,10 @@
refTime.toInstant().toEpochMilli(),
refTime.getZone().getId(),
localesString,
- detectLanguageTags));
+ detectLanguageTags,
+ entitiesToIdentify,
+ AnnotatorModel.AnnotationUsecase.SMART.getValue(),
+ isSerializedEntityDataEnabled));
for (AnnotatorModel.AnnotatedSpan span : annotations) {
final AnnotatorModel.ClassificationResult[] results =
span.getClassification();
@@ -326,7 +331,11 @@
for (int i = 0; i < results.length; i++) {
entityScores.put(results[i].getCollection(), results[i].getScore());
}
- builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores);
+ Bundle extras = new Bundle();
+ if (isSerializedEntityDataEnabled) {
+ ExtrasUtils.putEntities(extras, results);
+ }
+ builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores, extras);
}
final TextLinks links = builder.build();
final long endTimeMs = System.currentTimeMillis();
@@ -451,10 +460,6 @@
Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
List<ConversationAction> conversationActions = new ArrayList<>();
for (ActionsSuggestionsModel.ActionSuggestion nativeSuggestion : nativeSuggestions) {
- if (request.getMaxSuggestions() >= 0
- && conversationActions.size() == request.getMaxSuggestions()) {
- break;
- }
String actionType = nativeSuggestion.getActionType();
if (!expectedTypes.contains(actionType)) {
continue;
@@ -484,6 +489,10 @@
}
conversationActions =
ActionsSuggestionsHelper.removeActionsWithDuplicates(conversationActions);
+ if (request.getMaxSuggestions() >= 0
+ && conversationActions.size() > request.getMaxSuggestions()) {
+ conversationActions = conversationActions.subList(0, request.getMaxSuggestions());
+ }
String resultId = ActionsSuggestionsHelper.createResultId(
mContext,
request.getConversation(),
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 564cfdd..51ca805 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -933,12 +933,11 @@
final String language = ExtrasUtils.getEntityType(foreignLanguageExtra);
final float score = ExtrasUtils.getScore(foreignLanguageExtra);
final String model = ExtrasUtils.getModelName(foreignLanguageExtra);
- return new TextClassifierEvent.Builder(
- TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType)
+ return new TextClassifierEvent.LanguageDetectionEvent.Builder(eventType)
.setEventContext(classificationContext)
.setResultId(classification.getId())
.setEntityTypes(language)
- .setScore(score)
+ .setScores(score)
.setActionIndices(classification.getActions().indexOf(translateAction))
.setModelName(model)
.build();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1597ab7..54338bf 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -84,6 +84,7 @@
import android.service.chooser.ChooserTargetService;
import android.service.chooser.IChooserTargetResult;
import android.service.chooser.IChooserTargetService;
+import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.HashedStringCache;
@@ -1546,6 +1547,29 @@
float getModifiedScore();
ChooserTarget getChooserTarget();
+
+ /**
+ * Do not label as 'equals', since this doesn't quite work
+ * as intended with java 8.
+ */
+ default boolean isSimilar(ChooserTargetInfo other) {
+ if (other == null) return false;
+
+ ChooserTarget ct1 = getChooserTarget();
+ ChooserTarget ct2 = other.getChooserTarget();
+
+ // If either is null, there is not enough info to make an informed decision
+ // about equality, so just exit
+ if (ct1 == null || ct2 == null) return false;
+
+ if (ct1.getComponentName().equals(ct2.getComponentName())
+ && TextUtils.equals(getDisplayLabel(), other.getDisplayLabel())
+ && TextUtils.equals(getExtendedInfo(), other.getExtendedInfo())) {
+ return true;
+ }
+
+ return false;
+ }
}
/**
@@ -1624,6 +1648,7 @@
private final DisplayResolveInfo mSourceInfo;
private final ResolveInfo mBackupResolveInfo;
private final ChooserTarget mChooserTarget;
+ private final String mDisplayLabel;
private Drawable mBadgeIcon = null;
private CharSequence mBadgeContentDescription;
private Drawable mDisplayIcon;
@@ -1661,6 +1686,8 @@
mFillInIntent = null;
mFillInFlags = 0;
+
+ mDisplayLabel = sanitizeDisplayLabel(chooserTarget.getTitle());
}
private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) {
@@ -1673,6 +1700,14 @@
mFillInIntent = fillInIntent;
mFillInFlags = flags;
mModifiedScore = other.mModifiedScore;
+
+ mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle());
+ }
+
+ private String sanitizeDisplayLabel(CharSequence label) {
+ SpannableStringBuilder sb = new SpannableStringBuilder(label);
+ sb.clearSpans();
+ return sb.toString();
}
public boolean isSuspended() {
@@ -1811,7 +1846,7 @@
@Override
public CharSequence getDisplayLabel() {
- return mChooserTarget.getTitle();
+ return mDisplayLabel;
}
@Override
@@ -2229,8 +2264,6 @@
final float baseScore = getBaseScore(origTarget, isShortcutResult);
Collections.sort(targets, mBaseTargetComparator);
-
-
float lastScore = 0;
boolean shouldNotify = false;
for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) {
@@ -2305,8 +2338,15 @@
return false;
}
- final float newScore = chooserTargetInfo.getModifiedScore();
+ // Check for duplicates and abort if found
+ for (ChooserTargetInfo otherTargetInfo : mServiceTargets) {
+ if (chooserTargetInfo.isSimilar(otherTargetInfo)) {
+ return false;
+ }
+ }
+
int currentSize = mServiceTargets.size();
+ final float newScore = chooserTargetInfo.getModifiedScore();
for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) {
final ChooserTargetInfo serviceTarget = mServiceTargets.get(i);
if (serviceTarget == null) {
@@ -2393,15 +2433,11 @@
* @return true if the view width has changed
*/
public boolean calculateChooserTargetWidth(int width) {
- int targetMinWidth = getResources().getDimensionPixelSize(
- R.dimen.chooser_target_width);
-
if (width == 0) {
return false;
}
- int targetWidth = width / getMaxTargetsPerRow();
- int newWidth = Math.max(targetWidth, targetMinWidth);
+ int newWidth = width / getMaxTargetsPerRow();
if (newWidth != mChooserTargetWidth) {
mChooserTargetWidth = newWidth;
return true;
@@ -2622,12 +2658,24 @@
}
}
+ /**
+ * Need to merge CALLER + ranked STANDARD into a single row. All other types
+ * are placed into their own row as determined by their target type, and dividers
+ * are added in the list to separate each type.
+ */
+ int getRowType(int rowPosition) {
+ int positionType = mChooserListAdapter.getPositionTargetType(rowPosition);
+ if (positionType == ChooserListAdapter.TARGET_CALLER) {
+ return ChooserListAdapter.TARGET_STANDARD;
+ }
+
+ return positionType;
+ }
+
void bindViewHolder(int rowPosition, RowViewHolder holder) {
final int start = getFirstRowPosition(rowPosition);
- final int startType = mChooserListAdapter.getPositionTargetType(start);
-
- final int lastStartType = mChooserListAdapter.getPositionTargetType(
- getFirstRowPosition(rowPosition - 1));
+ final int startType = getRowType(start);
+ final int lastStartType = getRowType(getFirstRowPosition(rowPosition - 1));
final ViewGroup row = holder.getViewGroup();
@@ -2639,7 +2687,7 @@
int columnCount = holder.getColumnCount();
int end = start + columnCount - 1;
- while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) {
+ while (getRowType(end) != startType && end >= start) {
end--;
}
@@ -2691,14 +2739,15 @@
return row * getMaxTargetsPerRow();
}
- final int callerCount = mChooserListAdapter.getCallerTargetCount();
- final int callerRows = (int) Math.ceil((float) callerCount / getMaxTargetsPerRow());
- if (row < callerRows + serviceRows) {
+ final int callerAndRankedCount = mChooserListAdapter.getCallerTargetCount()
+ + mChooserListAdapter.getRankedTargetCount();
+ final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
+ if (row < callerAndRankedRows + serviceRows) {
return serviceCount + (row - serviceRows) * getMaxTargetsPerRow();
}
- return callerCount + serviceCount
- + (row - callerRows - serviceRows) * getMaxTargetsPerRow();
+ return callerAndRankedCount + serviceCount
+ + (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow();
}
public void handleScroll(View v, int y, int oldy) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index fd75f4f..21f8d87 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -45,6 +45,7 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
@@ -115,6 +116,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
/**
* Android-specific Window.
@@ -3926,4 +3928,15 @@
public WindowInsetsController getInsetsController() {
return mDecor.getWindowInsetsController();
}
+
+ @Override
+ public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) {
+ getViewRootImpl().setRootSystemGestureExclusionRects(rects);
+ }
+
+ @Override
+ @NonNull
+ public List<Rect> getSystemGestureExclusionRects() {
+ return getViewRootImpl().getRootSystemGestureExclusionRects();
+ }
}
diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java
new file mode 100644
index 0000000..2806ae2
--- /dev/null
+++ b/core/java/com/android/internal/util/TrafficStatsConstants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+/**
+ * Constants for traffic stats.
+ * @hide
+ */
+public class TrafficStatsConstants {
+ // These tags are used by the network stack to do traffic for its own purposes. Traffic
+ // tagged with these will be counted toward the network stack and must stay inside the
+ // range defined by
+ // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and
+ // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}.
+ public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01;
+ public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02;
+ public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03;
+
+ public static final int TAG_SYSTEM_NTP = 0xFFFFFF41;
+ public static final int TAG_SYSTEM_GPS = 0xFFFFFF44;
+ public static final int TAG_SYSTEM_PAC = 0xFFFFFF45;
+
+ // These tags are used by the network stack to do traffic on behalf of apps. Traffic
+ // tagged with these will be counted toward the app on behalf of which the network
+ // stack is doing this traffic. These values must stay inside the range defined by
+ // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and
+ // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}.
+ public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81;
+}
diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp
index a62dd7c..1e6d49e 100644
--- a/core/jni/android_nio_utils.cpp
+++ b/core/jni/android_nio_utils.cpp
@@ -18,51 +18,29 @@
#include "core_jni_helpers.h"
-namespace {
+namespace android {
-void* getPointer(JNIEnv *_env, jobject buffer, jarray *array, void** elements) {
- assert(array);
- jint position;
- jint limit;
- jint elementSizeShift;
- jlong pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
+AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit)
+ : fEnv(env), fCommit(commit) {
+ jlong pointer = jniGetNioBufferPointer(fEnv, nioBuffer);
if (pointer != 0L) {
- *array = nullptr;
- *elements = nullptr;
- pointer += position << elementSizeShift;
- return reinterpret_cast<void*>(pointer);
+ // Buffer is backed by a direct buffer.
+ fArray = nullptr;
+ fElements = nullptr;
+ fPointer = reinterpret_cast<void*>(pointer);
+ } else {
+ // Buffer is backed by a managed array.
+ jint byteOffset = jniGetNioBufferBaseArrayOffset(fEnv, nioBuffer);
+ fArray = jniGetNioBufferBaseArray(fEnv, nioBuffer);
+ fElements = fEnv->GetPrimitiveArrayCritical(fArray, /* isCopy= */ nullptr);
+ fPointer = reinterpret_cast<void*>(reinterpret_cast<char*>(fElements) + byteOffset);
}
- jint offset = jniGetNioBufferBaseArrayOffset(_env, buffer);
- *array = jniGetNioBufferBaseArray(_env, buffer);
- *elements = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
- return reinterpret_cast<void*>(reinterpret_cast<char*>(*elements) + offset);
}
-void releasePointer(JNIEnv *_env, jarray array, void *elements, jboolean commit) {
- _env->ReleasePrimitiveArrayCritical(array, elements, commit ? 0 : JNI_ABORT);
-}
-
-} // namespace
-
-void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) {
- void* elements;
- return getPointer(_env, buffer, array, &elements);
-}
-
-void android::nio_releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) {
- releasePointer(_env, array, data, commit);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-android::AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) {
- fEnv = env;
- fCommit = commit;
- fPointer = getPointer(env, nioBuffer, &fArray, &fElements);
-}
-
-android::AutoBufferPointer::~AutoBufferPointer() {
+AutoBufferPointer::~AutoBufferPointer() {
if (nullptr != fArray) {
- releasePointer(fEnv, fArray, fElements, fCommit);
+ fEnv->ReleasePrimitiveArrayCritical(fArray, fElements, fCommit ? 0 : JNI_ABORT);
}
}
+
+} // namespace android
diff --git a/core/jni/android_nio_utils.h b/core/jni/android_nio_utils.h
index 7c9acd2..aa75dd0 100644
--- a/core/jni/android_nio_utils.h
+++ b/core/jni/android_nio_utils.h
@@ -22,51 +22,58 @@
namespace android {
/**
- * Given an nio.Buffer, return a pointer to it, beginning at its current
- * position. The returned pointer is only valid for the current JNI stack-frame.
- * For performance, it does not create any global references, so the getPointer
- * (and releasePointer if array is returned non-null) must be done in the
- * same JNI stack-frame.
+ * Class providing scoped access to the memory backing a java.nio.Buffer instance.
*
- * @param env The current JNI env
- * @param buffer The nio.Buffer object
- * @param array REQUIRED. Output. If on return it is set to non-null, then
- * nio_releasePointer must be called with the array
- * and the returned pointer when the caller is through with it.
- * If on return it is set to null, do not call
- * nio_releasePointer.
- * @return The pointer to the memory in the buffer object
- */
-void* nio_getPointer(JNIEnv *env, jobject buffer, jarray *array);
-
-/**
- * Call this if android_nio_getPointer returned non-null in its array parameter.
- * Pass that array and the returned pointer when you are done accessing the
- * pointer. If called (i.e. array is non-null), it must be called in the same
- * JNI stack-frame as getPointer
+ * Instances of this class should only be allocated on the stack as heap allocation is not
+ * supported.
*
- * @param env The current JNI env
- * @param buffer The array returned from android_nio_getPointer (!= null)
- * @param pointer The pointer returned by android_nio_getPointer
- * @param commit JNI_FALSE if the pointer was just read, and JNI_TRUE if
- * the pointer was written to.
+ * Instances of this class do not create any global references for performance reasons.
*/
-void nio_releasePointer(JNIEnv *env, jarray array, void *pointer,
- jboolean commit);
-
-class AutoBufferPointer {
+class AutoBufferPointer final {
public:
+ /** Constructor for an AutoBufferPointer instance.
+ *
+ * @param env The current JNI env
+ * @param nioBuffer Instance of a java.nio.Buffer whose memory will be accessed.
+ * @param commit JNI_TRUE if the underlying memory will be updated and should be
+ * copied back to the managed heap. JNI_FALSE if the data will
+ * not be modified or the modifications may be discarded.
+ *
+ * The commit parameter is only applicable if the buffer is backed by a managed heap
+ * array and the runtime had to provide a copy of the data rather than the original data.
+ */
AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit);
+
+ /** Destructor for an AutoBufferPointer instance.
+ *
+ * Releases critical managed heap array pointer if acquired.
+ */
~AutoBufferPointer();
+ /**
+ * Returns a pointer to the current position of the buffer provided to the constructor. This
+ * pointer is only valid whilst the AutoBufferPointer instance remains in scope.
+ */
void* pointer() const { return fPointer; }
private:
- JNIEnv* fEnv;
- void* fPointer; // pointer to current buffer position.
- void* fElements; // pointer to array element 0 (may be directly in fArray or a copy).
- jarray fArray; // pointer to array on managed heap.
- jboolean fCommit; // commit data to source if required (when fElements is a copy of fArray).
+ JNIEnv* const fEnv;
+ void* fPointer; // Pointer to current buffer position when constructed.
+ void* fElements; // Pointer to array element 0 (null if buffer is direct, may be
+ // within fArray or point to a copy of the array).
+ jarray fArray; // Pointer to array on managed heap.
+ const jboolean fCommit; // Flag to commit data to source (when fElements is a copy of fArray).
+
+ // Unsupported constructors and operators.
+ AutoBufferPointer() = delete;
+ AutoBufferPointer(AutoBufferPointer&) = delete;
+ AutoBufferPointer& operator=(AutoBufferPointer&) = delete;
+ static void* operator new(std::size_t);
+ static void* operator new[](std::size_t);
+ static void* operator new(std::size_t, void*);
+ static void* operator new[](std::size_t, void*);
+ static void operator delete(void*, std::size_t);
+ static void operator delete[](void*, std::size_t);
};
} /* namespace android */
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index f599913..e2214d1 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -73,6 +73,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <bionic_malloc.h>
#include <cutils/ashmem.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
@@ -499,12 +500,9 @@
}
}
-// The debug malloc library needs to know whether it's the zygote or a child.
-extern "C" int gMallocLeakZygoteChild;
-
static void PreApplicationInit() {
// The child process sets this to indicate it's not the zygote.
- gMallocLeakZygoteChild = 1;
+ android_mallopt(M_SET_ZYGOTE_CHILD, nullptr, 0);
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto
index c357065..566861b 100644
--- a/core/proto/android/os/enums.proto
+++ b/core/proto/android/os/enums.proto
@@ -78,6 +78,41 @@
TEMPERATURE_TYPE_NPU = 9;
}
+// Device throttling severity
+// These constants are defined in hardware/interfaces/thermal/2.0/types.hal.
+// Any change to the types in the thermal hal should be made here as well.
+enum ThrottlingSeverityEnum {
+ // Not under throttling.
+ NONE = 0;
+ // Light throttling where UX is not impacted.
+ LIGHT = 1;
+ // Moderate throttling where UX is not largely impacted.
+ MODERATE = 2;
+ // Severe throttling where UX is largely impacted.
+ // Similar to 1.0 throttlingThreshold.
+ SEVERE = 3;
+ // Platform has done everything to reduce power.
+ CRITICAL = 4;
+ // Key components in platform are shutting down due to thermal condition.
+ // Device functionalities will be limited.
+ EMERGENCY = 5;
+ // Need shutdown immediately.
+ SHUTDOWN = 6;
+};
+
+// Device cooling device types.
+// These constants are defined in hardware/interfaces/thermal/2.0/types.hal.
+// Any change to the types in the thermal hal should be made here as well.
+enum CoolingTypeEnum {
+ FAN = 0;
+ BATTERY = 1;
+ CPU = 2;
+ GPU = 3;
+ MODEM = 4;
+ NPU = 5;
+ COMPONENT = 6;
+};
+
// Wakelock types, primarily used by android/os/PowerManager.java.
enum WakeLockLevelEnum {
// NOTE: Wake lock levels were previously defined as a bit field, except
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cc3b3a4..8714bf2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -828,6 +828,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
+ android:permissionFlags="softRestricted|immutablyRestricted"
android:protectionLevel="dangerous" />
<!-- Allows an application to write to external storage.
@@ -848,6 +849,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
+ android:permissionFlags="softRestricted|immutablyRestricted"
android:protectionLevel="dangerous" />
<!-- Allows an application to access any geographic locations persisted in the
diff --git a/core/res/res/drawable/chooser_row_layer_list.xml b/core/res/res/drawable/chooser_row_layer_list.xml
index 0fb26e1..0800815 100644
--- a/core/res/res/drawable/chooser_row_layer_list.xml
+++ b/core/res/res/drawable/chooser_row_layer_list.xml
@@ -17,7 +17,7 @@
*/
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:bottom="-2dp" android:left="-2dp" android:right="-2dp">
+ <item android:bottom="-5dp" android:left="-5dp" android:right="-5dp">
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="@color/chooser_row_divider"/>
</shape>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index bfa57e4..bbc784a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -328,6 +328,12 @@
would be granted. The weak grant depends on the permission.
-->
<flag name="softRestricted" value="0x8" />
+ <!-- This permission is restricted immutably which means that its
+ restriction state may be specified only on the first install of
+ the app and will stay in this initial whitelist state until
+ the app is uninstalled.
+ -->
+ <flag name="immutablyRestricted" value="0x10" />
</attr>
<!-- Specified the name of a group that this permission is associated
@@ -1707,17 +1713,18 @@
See {@link android.media.AudioPlaybackCaptureConfiguration} for more detail.
-->
<attr name="allowAudioPlaybackCapture" format="boolean" />
- <!-- If {@code true} this app allows shared/external storage media to be
- a sandboxed view that only contains files owned by the app.
- <p>
- Sandboxed apps can continue to discover and read media belonging to other
- apps via {@code MediaStore}.
+ <!-- If {@code true} this app would like to run under the legacy storage
+ model. Note that this may not always be respected due to policy or
+ backwards compatibility reasons.
+
+ <p>Apps not requesting legacy storage can continue to discover and
+ read media belonging to other apps via {@code MediaStore}.
<p>
The default value is:
- - {@code true} for apps with targetSdkVersion >= 29 (Q).
- - {@code false} for apps with targetSdkVersion < 29.
+ - {@code false} for apps with targetSdkVersion >= 29 (Q).
+ - {@code true} for apps with targetSdkVersion < 29.
-->
- <attr name="allowExternalStorageSandbox" format="boolean" />
+ <attr name="requestLegacyExternalStorage" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5268189..4d6fda9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3483,6 +3483,12 @@
<item>com.android.messaging</item>
</string-array>
+ <!-- An array of packages that can make sound on the ringer stream in priority-only DND
+ mode -->
+ <string-array translatable="false" name="config_priorityOnlyDndExemptPackages">
+ <item>com.android.dialer</item>
+ </string-array>
+
<!-- An array of packages which can listen for notifications on low ram devices. -->
<string-array translatable="false" name="config_allowedManagedServicesOnLowRamDevices" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b7d61c8..a7af144 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2942,7 +2942,7 @@
<public name="allowClearUserDataOnFailedRestore"/>
<public name="allowAudioPlaybackCapture"/>
<public name="secureElementName" />
- <public name="allowExternalStorageSandbox"/>
+ <public name="requestLegacyExternalStorage"/>
<public name="ensuringStatusBarContrastWhenTransparent" />
<public name="ensuringNavigationBarContrastWhenTransparent" />
<public name="identifier" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 94b5da6..924b036 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3188,6 +3188,7 @@
<java-symbol type="array" name="config_convert_to_emergency_number_map" />
<java-symbol type="array" name="config_nonBlockableNotificationPackages" />
+ <java-symbol type="array" name="config_priorityOnlyDndExemptPackages" />
<java-symbol type="array" name="config_allowedManagedServicesOnLowRamDevices" />
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index 4bee243..2280cf1 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -23,6 +23,8 @@
import junit.framework.TestCase;
import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
public class BitmapTest extends TestCase {
@@ -266,10 +268,11 @@
}
@SmallTest
- public void testCopyWithDirectBuffer() {
+ public void testCopyWithDirectByteBuffer() {
// Initialize Bitmap
final int width = 2;
final int height = 2;
+ final int bytesPerPixel = 2;
Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
@@ -277,7 +280,8 @@
// of bitmap.
final int pad = 1;
final byte padValue = 0x5a;
- final int bufferSize = pad + width * height * 2 + pad;
+ final int bytesPerElement = 1;
+ final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize);
// Write padding
@@ -287,7 +291,8 @@
// Copy bitmap
directBuffer.position(pad);
bm1.copyPixelsToBuffer(directBuffer);
- assertEquals(directBuffer.position(), pad + width * height * 2);
+ assertEquals(directBuffer.position(),
+ pad + width * height * bytesPerPixel / bytesPerElement);
// Check padding
assertEquals(directBuffer.get(0), padValue);
@@ -301,10 +306,89 @@
}
@SmallTest
- public void testCopyWithHeapBuffer() {
+ public void testCopyWithDirectShortBuffer() {
// Initialize Bitmap
final int width = 2;
final int height = 2;
+ final int bytesPerPixel = 2;
+ Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
+
+ // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
+ // of bitmap.
+ final int pad = 1;
+ final short padValue = 0x55aa;
+ final int bytesPerElement = 2;
+ final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
+ ShortBuffer directBuffer =
+ ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asShortBuffer();
+
+ // Write padding
+ directBuffer.put(0, padValue);
+ directBuffer.put(directBuffer.limit() - 1, padValue);
+
+ // Copy bitmap
+ directBuffer.position(pad);
+ bm1.copyPixelsToBuffer(directBuffer);
+ assertEquals(directBuffer.position(),
+ pad + width * height * bytesPerPixel / bytesPerElement);
+
+ // Check padding
+ assertEquals(directBuffer.get(0), padValue);
+ assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue);
+
+ // Create bitmap from heap buffer and check match.
+ directBuffer.position(pad);
+ Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm2.copyPixelsFromBuffer(directBuffer);
+ assertTrue(bm2.sameAs(bm1));
+ }
+
+ @SmallTest
+ public void testCopyWithDirectIntBuffer() {
+ // Initialize Bitmap
+ final int width = 2;
+ final int height = 2;
+ final int bytesPerPixel = 2;
+ Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
+
+ // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
+ // of bitmap.
+ final int pad = 1;
+ final int padValue = 0x55aa5a5a;
+ final int bytesPerElement = 4;
+ final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
+ IntBuffer directBuffer =
+ ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asIntBuffer();
+
+ // Write padding
+ directBuffer.put(0, padValue);
+ directBuffer.put(directBuffer.limit() - 1, padValue);
+
+ // Copy bitmap
+ directBuffer.position(pad);
+ bm1.copyPixelsToBuffer(directBuffer);
+ assertEquals(directBuffer.position(),
+ pad + width * height * bytesPerPixel / bytesPerElement);
+
+ // Check padding
+ assertEquals(directBuffer.get(0), padValue);
+ assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue);
+
+ // Create bitmap from heap buffer and check match.
+ directBuffer.position(pad);
+ Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm2.copyPixelsFromBuffer(directBuffer);
+ assertTrue(bm2.sameAs(bm1));
+ }
+
+ @SmallTest
+ public void testCopyWithHeapByteBuffer() {
+ // Initialize Bitmap
+ final int width = 2;
+ final int height = 2;
+ final int bytesPerPixel = 2;
Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
@@ -312,7 +396,8 @@
// of bitmap.
final int pad = 1;
final byte padValue = 0x5a;
- final int bufferSize = pad + width * height * 2 + pad;
+ final int bytesPerElement = 1;
+ final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
ByteBuffer heapBuffer = ByteBuffer.allocate(bufferSize);
// Write padding
@@ -322,7 +407,81 @@
// Copy bitmap
heapBuffer.position(pad);
bm1.copyPixelsToBuffer(heapBuffer);
- assertEquals(heapBuffer.position(), pad + width * height * 2);
+ assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement);
+
+ // Check padding
+ assertEquals(heapBuffer.get(0), padValue);
+ assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue);
+
+ // Create bitmap from heap buffer and check match.
+ heapBuffer.position(pad);
+ Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm2.copyPixelsFromBuffer(heapBuffer);
+ assertTrue(bm2.sameAs(bm1));
+ }
+
+ @SmallTest
+ public void testCopyWithHeapShortBuffer() {
+ // Initialize Bitmap
+ final int width = 2;
+ final int height = 2;
+ final int bytesPerPixel = 2;
+ Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
+
+ // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
+ // of bitmap.
+ final int pad = 1;
+ final short padValue = 0x55aa;
+ final int bytesPerElement = 2;
+ final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
+ ShortBuffer heapBuffer = ShortBuffer.allocate(bufferSize);
+
+ // Write padding
+ heapBuffer.put(0, padValue);
+ heapBuffer.put(heapBuffer.limit() - 1, padValue);
+
+ // Copy bitmap
+ heapBuffer.position(pad);
+ bm1.copyPixelsToBuffer(heapBuffer);
+ assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement);
+
+ // Check padding
+ assertEquals(heapBuffer.get(0), padValue);
+ assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue);
+
+ // Create bitmap from heap buffer and check match.
+ heapBuffer.position(pad);
+ Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm2.copyPixelsFromBuffer(heapBuffer);
+ assertTrue(bm2.sameAs(bm1));
+ }
+
+ @SmallTest
+ public void testCopyWithHeapIntBuffer() {
+ // Initialize Bitmap
+ final int width = 2;
+ final int height = 2;
+ final int bytesPerPixel = 2;
+ Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
+
+ // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
+ // of bitmap.
+ final int pad = 1;
+ final int padValue = 0x55aa5a5a;
+ final int bytesPerElement = 4;
+ final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
+ IntBuffer heapBuffer = IntBuffer.allocate(bufferSize);
+
+ // Write padding
+ heapBuffer.put(0, padValue);
+ heapBuffer.put(heapBuffer.limit() - 1, padValue);
+
+ // Copy bitmap
+ heapBuffer.position(pad);
+ bm1.copyPixelsToBuffer(heapBuffer);
+ assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement);
// Check padding
assertEquals(heapBuffer.get(0), padValue);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 433991e..aeb8949 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -362,6 +362,38 @@
}
@Test
+ public void testGenerateLinks_entityData() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The number is +12122537077.";
+ Bundle extras = new Bundle();
+ ExtrasUtils.putIsSerializedEntityDataEnabled(extras, true);
+ TextLinks.Request request = new TextLinks.Request.Builder(text).setExtras(extras).build();
+
+ TextLinks textLinks = mClassifier.generateLinks(request);
+
+ Truth.assertThat(textLinks.getLinks()).hasSize(1);
+ TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
+ List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
+ Truth.assertThat(entities).hasSize(1);
+ Bundle entity = entities.get(0);
+ Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_PHONE);
+ }
+
+ @Test
+ public void testGenerateLinks_entityData_disabled() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The number is +12122537077.";
+ TextLinks.Request request = new TextLinks.Request.Builder(text).build();
+
+ TextLinks textLinks = mClassifier.generateLinks(request);
+
+ Truth.assertThat(textLinks.getLinks()).hasSize(1);
+ TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
+ List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
+ Truth.assertThat(entities).isNull();
+ }
+
+ @Test
public void testDetectLanguage() {
if (isTextClassifierDisabled()) return;
String text = "This is English text";
@@ -380,7 +412,7 @@
}
@Test
- public void testSuggestConversationActions_textReplyOnly_maxThree() {
+ public void testSuggestConversationActions_textReplyOnly_maxOne() {
if (isTextClassifierDisabled()) return;
ConversationActions.Message message =
new ConversationActions.Message.Builder(
@@ -399,12 +431,11 @@
.build();
ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- assertTrue(conversationActions.getConversationActions().size() > 0);
- for (ConversationAction conversationAction :
- conversationActions.getConversationActions()) {
- assertThat(conversationAction,
- isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
- }
+ Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
+ ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
+ Truth.assertThat(conversationAction.getType()).isEqualTo(
+ ConversationAction.TYPE_TEXT_REPLY);
+ Truth.assertThat(conversationAction.getTextReply()).isNotNull();
}
@Test
@@ -493,6 +524,24 @@
ExtrasUtils.getSerializedEntityData(conversationAction.getExtras())).isNotEmpty();
}
+ @Test
+ public void testSuggetsConversationActions_deduplicate() {
+ if (isTextClassifierDisabled()) return;
+ ConversationActions.Message message =
+ new ConversationActions.Message.Builder(
+ ConversationActions.Message.PERSON_USER_OTHERS)
+ .setText("a@android.com b@android.com")
+ .build();
+ ConversationActions.Request request =
+ new ConversationActions.Request.Builder(Collections.singletonList(message))
+ .setMaxSuggestions(3)
+ .build();
+
+ ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
+
+ Truth.assertThat(conversationActions.getConversationActions()).isEmpty();
+ }
+
private boolean isTextClassifierDisabled() {
return mClassifier == null || mClassifier == TextClassifier.NO_OP;
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
index 1980a60..2c540e5 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -49,8 +49,6 @@
public class TextClassifierEventTronLoggerTest {
private static final String WIDGET_TYPE = "notification";
private static final String PACKAGE_NAME = "pkg";
- private static final long EVENT_TIME = System.currentTimeMillis();
-
@Mock
private MetricsLogger mMetricsLogger;
@@ -68,13 +66,11 @@
TextClassificationContext textClassificationContext =
new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE)
.build();
- TextClassifierEvent textClassifierEvent =
- new TextClassifierEvent.Builder(
- TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
+ TextClassifierEvent.ConversationActionsEvent textClassifierEvent =
+ new TextClassifierEvent.ConversationActionsEvent.Builder(
TextClassifierEvent.TYPE_SMART_ACTION)
.setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
- .setScore(0.5f)
- .setEventTime(EVENT_TIME)
+ .setScores(0.5f)
.setEventContext(textClassificationContext)
.build();
@@ -83,10 +79,8 @@
ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class);
Mockito.verify(mMetricsLogger).write(captor.capture());
LogMaker logMaker = captor.getValue();
- assertThat(logMaker.getCategory()).isEqualTo(
- CONVERSATION_ACTIONS);
- assertThat(logMaker.getSubtype()).isEqualTo(
- ACTION_TEXT_SELECTION_SMART_SHARE);
+ assertThat(logMaker.getCategory()).isEqualTo(CONVERSATION_ACTIONS);
+ assertThat(logMaker.getSubtype()).isEqualTo(ACTION_TEXT_SELECTION_SMART_SHARE);
assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
.isEqualTo(ConversationAction.TYPE_CALL_PHONE);
assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
@@ -101,9 +95,8 @@
@Test
public void testWriteEvent_unsupportedCategory() {
- TextClassifierEvent textClassifierEvent =
- new TextClassifierEvent.Builder(
- TextClassifierEvent.CATEGORY_SELECTION,
+ TextClassifierEvent.TextSelectionEvent textClassifierEvent =
+ new TextClassifierEvent.TextSelectionEvent.Builder(
TextClassifierEvent.TYPE_SMART_ACTION)
.build();
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
new file mode 100644
index 0000000..ae3ff86
--- /dev/null
+++ b/core/tests/mockingcoretests/Android.bp
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test {
+ name: "FrameworksMockingCoreTests",
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ "testables",
+ "ub-uiautomator",
+ ],
+
+ libs: [
+ "android.test.base",
+ "android.test.mock",
+ "android.test.runner",
+ ],
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+}
diff --git a/core/tests/mockingcoretests/AndroidManifest.xml b/core/tests/mockingcoretests/AndroidManifest.xml
new file mode 100644
index 0000000..b9ee085
--- /dev/null
+++ b/core/tests/mockingcoretests/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.frameworks.mockingcoretests"
+ android:sharedUserId="com.android.uid.test">
+
+ <application android:supportsRtl="true" android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="android.app.activity.ActivityThreadClientTest$TestActivity"
+ android:exported="true">
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.mockingcoretests"
+ android:label="Frameworks Mocking Core Tests" />
+</manifest>
diff --git a/core/tests/mockingcoretests/AndroidTest.xml b/core/tests/mockingcoretests/AndroidTest.xml
new file mode 100644
index 0000000..47aa410
--- /dev/null
+++ b/core/tests/mockingcoretests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+<configuration description="Runs Frameworks Mocking Core Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksMockingCoreTests.apk" />
+ </target_preparer>
+ <option name="test-tag" value="FrameworksMockingCoreTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.mockingcoretests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/core/tests/mockingcoretests/README b/core/tests/mockingcoretests/README
new file mode 100644
index 0000000..c0f65c2
--- /dev/null
+++ b/core/tests/mockingcoretests/README
@@ -0,0 +1,33 @@
+* Copyright (C) 2019 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.
+
+
+INTRODUCTION
+
+The Android platform core tests that require additional mocking capabilities and use Extended
+Mockito, such as the ability to stub static methods. ExtendedMockito is not fully compatible with
+regular Mockito library, so tests that use it have to be verified and adapted. For that reason a
+separate module is used instead of adding to FrameworksCoreTests.
+
+For more information about ExtendedMockito see documentation of
+com.android.dx.mockito.inline.extended.ExtendedMockito class.
+
+See ../coretests/README for more information on FrameworksCoreTests.
+
+
+INSTRUCTIONS
+
+To build, install and run:
+
+ atest FrameworksMockingCoreTests
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
new file mode 100644
index 0000000..86d55ea
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2019 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.app.activity;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.IActivityTaskManager;
+import android.app.servertransaction.PendingTransactionActions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Test for verifying {@link android.app.ActivityThread} class.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksMockingCoreTests:android.app.activity.ActivityThreadClientTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class ActivityThreadClientTest {
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnCreate() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ clientSession.launchActivity(r);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnCreate_Finished() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ Activity activity = clientSession.launchActivity(r);
+ activity.finish();
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnStart() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ assertEquals(ON_START, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_START, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnStart_Finished() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ Activity activity = clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ activity.finish();
+ assertEquals(ON_START, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_START, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnResume() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ clientSession.resumeActivity(r);
+ assertEquals(ON_RESUME, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_START, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnPause() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ clientSession.resumeActivity(r);
+ clientSession.pauseActivity(r);
+ assertEquals(ON_PAUSE, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_START, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testWindowVisibilityChange_OnStop() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ clientSession.resumeActivity(r);
+ clientSession.pauseActivity(r);
+ clientSession.stopActivity(r);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, true);
+ assertEquals(ON_START, r.getLifecycleState());
+
+ clientSession.changeVisibility(r, false);
+ assertEquals(ON_STOP, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testLifecycleAfterFinished_OnCreate() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ Activity activity = clientSession.launchActivity(r);
+ activity.finish();
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.startActivity(r);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.resumeActivity(r);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.pauseActivity(r);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.stopActivity(r);
+ assertEquals(ON_CREATE, r.getLifecycleState());
+
+ clientSession.destroyActivity(r);
+ assertEquals(ON_DESTROY, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testLifecycleAfterFinished_OnStart() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ Activity activity = clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ activity.finish();
+ assertEquals(ON_START, r.getLifecycleState());
+
+ clientSession.resumeActivity(r);
+ assertEquals(ON_START, r.getLifecycleState());
+
+ clientSession.pauseActivity(r);
+ assertEquals(ON_START, r.getLifecycleState());
+
+ clientSession.stopActivity(r);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.destroyActivity(r);
+ assertEquals(ON_DESTROY, r.getLifecycleState());
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ public void testLifecycleAfterFinished_OnResume() throws Exception {
+ try (ClientMockSession clientSession = new ClientMockSession()) {
+ ActivityClientRecord r = clientSession.stubActivityRecord();
+
+ Activity activity = clientSession.launchActivity(r);
+ clientSession.startActivity(r);
+ clientSession.resumeActivity(r);
+ activity.finish();
+ assertEquals(ON_RESUME, r.getLifecycleState());
+
+ clientSession.pauseActivity(r);
+ assertEquals(ON_PAUSE, r.getLifecycleState());
+
+ clientSession.stopActivity(r);
+ assertEquals(ON_STOP, r.getLifecycleState());
+
+ clientSession.destroyActivity(r);
+ assertEquals(ON_DESTROY, r.getLifecycleState());
+ }
+ }
+
+ private class ClientMockSession implements AutoCloseable {
+ private MockitoSession mMockSession;
+ private ActivityThread mThread;
+
+ private ClientMockSession() throws RemoteException {
+ mThread = ActivityThread.currentActivityThread();
+ mMockSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(ActivityTaskManager.class)
+ .spyStatic(WindowManagerGlobal.class)
+ .startMocking();
+ doReturn(Mockito.mock(WindowManagerGlobal.class))
+ .when(WindowManagerGlobal::getInstance);
+ IActivityTaskManager mockAtm = Mockito.mock(IActivityTaskManager.class);
+ doReturn(mockAtm).when(ActivityTaskManager::getService);
+ when(mockAtm.finishActivity(any(), anyInt(), any(), anyInt())).thenReturn(true);
+ }
+
+ private Activity launchActivity(ActivityClientRecord r) {
+ return mThread.handleLaunchActivity(r, null /* pendingActions */,
+ null /* customIntent */);
+ }
+
+ private void startActivity(ActivityClientRecord r) {
+ mThread.handleStartActivity(r, null /* pendingActions */);
+ }
+
+ private void resumeActivity(ActivityClientRecord r) {
+ mThread.handleResumeActivity(r.token, true /* finalStateRequest */,
+ true /* isForward */, "test");
+ }
+
+ private void pauseActivity(ActivityClientRecord r) {
+ mThread.handlePauseActivity(r.token, false /* finished */,
+ false /* userLeaving */, 0 /* configChanges */, null /* pendingActions */,
+ "test");
+ }
+
+ private void stopActivity(ActivityClientRecord r) {
+ mThread.handleStopActivity(r.token, false /* show */, 0 /* configChanges */,
+ new PendingTransactionActions(), false /* finalStateRequest */, "test");
+ }
+
+ private void destroyActivity(ActivityClientRecord r) {
+ mThread.handleDestroyActivity(r.token, true /* finishing */, 0 /* configChanges */,
+ false /* getNonConfigInstance */, "test");
+ }
+
+ private void changeVisibility(ActivityClientRecord r, boolean show) {
+ mThread.handleWindowVisibility(r.token, show);
+ }
+
+ private ActivityClientRecord stubActivityRecord() {
+ ComponentName component = new ComponentName(
+ InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class);
+ ActivityInfo info = new ActivityInfo();
+ info.packageName = component.getPackageName();
+ info.name = component.getClassName();
+ info.exported = true;
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.packageName = info.packageName;
+ info.applicationInfo.uid = UserHandle.myUserId();
+
+ return new ActivityClientRecord(new Binder(), Intent.makeMainActivity(component),
+ 0 /* ident */, info, new Configuration(),
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */,
+ null /* voiceInteractor */, null /* state */, null /* persistentState */,
+ null /* pendingResults */, null /* pendingNewIntents */, true /* isForward */,
+ null /* profilerInfo */, mThread /* client */);
+ }
+
+ @Override
+ public void close() {
+ mMockSession.finishMocking();
+ }
+ }
+
+ // Test activity
+ public static class TestActivity extends Activity {
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0e957df..c54208b 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,6 +180,9 @@
<assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" />
+ <split-permission name="android.permission.ACCESS_FINE_LOCATION">
+ <new-permission name="android.permission.ACCESS_COARSE_LOCATION" />
+ </split-permission>
<split-permission name="android.permission.WRITE_EXTERNAL_STORAGE">
<new-permission name="android.permission.READ_EXTERNAL_STORAGE" />
</split-permission>
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 11e4cb8..2ad2e76 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3510,6 +3510,7 @@
ResStringPool keyStrings;
size_t typeIdOffset;
+ bool definesOverlayable = false;
};
// A group of objects describing a particular resource package.
@@ -6861,6 +6862,10 @@
ALOGW("Found multiple library tables, ignoring...");
}
} else {
+ if (ctype == RES_TABLE_OVERLAYABLE_TYPE) {
+ package->definesOverlayable = true;
+ }
+
status_t err = validate_chunk(chunk, sizeof(ResChunk_header),
endPos, "ResTable_package:unknown");
if (err != NO_ERROR) {
@@ -7138,6 +7143,11 @@
return UNKNOWN_ERROR;
}
+ // Idmap is not aware of overlayable, exit since policy checks can't be done
+ if (targetResTable.mPackageGroups[0]->packages[0]->definesOverlayable) {
+ return UNKNOWN_ERROR;
+ }
+
const ResTable_package* targetPackageStruct =
targetResTable.mPackageGroups[0]->packages[0]->package;
const size_t tmpNameSize = arraysize(targetPackageStruct->name);
diff --git a/media/Android.bp b/media/Android.bp
index 5b7b26c..70dacb2 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -20,10 +20,6 @@
],
},
- static_libs: [
- "mediaplayer2-protos",
- ],
-
permitted_packages: [
"android.media",
],
@@ -43,7 +39,6 @@
name: "updatable-media-srcs",
srcs: [
":mediasession2-srcs",
- ":mediaplayer2-srcs",
],
}
@@ -51,7 +46,6 @@
name: "updatable-media-srcs-without-aidls",
srcs : [
":mediasession2-srcs-without-aidls",
- ":mediaplayer2-srcs",
],
}
@@ -109,8 +103,7 @@
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
- "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi " +
- " --show-annotation android.annotation.TestApi "
+ "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi "
droidstubs {
name: "updatable-media-stubs",
diff --git a/media/apex/java/android/media/BufferingParams.java b/media/apex/java/android/media/BufferingParams.java
index 83594d4..943f142 100644
--- a/media/apex/java/android/media/BufferingParams.java
+++ b/media/apex/java/android/media/BufferingParams.java
@@ -17,7 +17,6 @@
package android.media;
import android.annotation.IntDef;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -64,7 +63,6 @@
* <p>Users should use {@link Builder} to change {@link BufferingParams}.
* @hide
*/
-@TestApi
public final class BufferingParams implements Parcelable {
private static final int BUFFERING_NO_MARK = -1;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d347137..f8e43437 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4523,6 +4523,7 @@
*/
/** @hide */
+ @TestApi
@SystemApi
public static final int SUCCESS = AudioSystem.SUCCESS;
/**
diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java
deleted file mode 100644
index f704acd..0000000
--- a/media/java/android/media/MiniThumbFile.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2009 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.media;
-
-import android.annotation.UnsupportedAppUsage;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.util.Log;
-
-import dalvik.system.VMRuntime;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.util.Hashtable;
-
-/**
- * This class handles the mini-thumb file. A mini-thumb file consists
- * of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the
- * following format:
- *
- * 1 byte status (0 = empty, 1 = mini-thumb available)
- * 8 bytes magic (a magic number to match what's in the database)
- * 4 bytes data length (LEN)
- * LEN bytes jpeg data
- * (the remaining bytes are unused)
- *
- * @hide This file is shared between MediaStore and MediaProvider and should remained internal use
- * only.
- * @deprecated thumbnails are now maintained in separate files, and this file
- * format is no longer used.
- */
-@Deprecated
-public class MiniThumbFile {
- private static final String TAG = "MiniThumbFile";
- private static final int MINI_THUMB_DATA_FILE_VERSION = 4;
- public static final int BYTES_PER_MINTHUMB = 10000;
- private static final int HEADER_SIZE = 1 + 8 + 4;
- private Uri mUri;
- private RandomAccessFile mMiniThumbFile;
- private FileChannel mChannel;
- private ByteBuffer mBuffer;
- private ByteBuffer mEmptyBuffer;
- private static final Hashtable<String, MiniThumbFile> sThumbFiles =
- new Hashtable<String, MiniThumbFile>();
-
- /**
- * We store different types of thumbnails in different files. To remain backward compatibility,
- * we should hashcode of content://media/external/images/media remains the same.
- */
- @UnsupportedAppUsage
- public static synchronized void reset() {
- for (MiniThumbFile file : sThumbFiles.values()) {
- file.deactivate();
- }
- sThumbFiles.clear();
- }
-
- public static synchronized MiniThumbFile instance(Uri uri) {
- if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- throw new UnsupportedOperationException();
- }
- String type = uri.getPathSegments().get(1);
- MiniThumbFile file = sThumbFiles.get(type);
- // Log.v(TAG, "get minithumbfile for type: "+type);
- if (file == null) {
- file = new MiniThumbFile(
- Uri.parse("content://media/external/" + type + "/media"));
- sThumbFiles.put(type, file);
- }
-
- return file;
- }
-
- private String randomAccessFilePath(int version) {
- String directoryName =
- Environment.getExternalStorageDirectory().toString()
- + "/DCIM/.thumbnails";
- return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode();
- }
-
- private void removeOldFile() {
- String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1);
- File oldFile = new File(oldPath);
- if (oldFile.exists()) {
- try {
- oldFile.delete();
- } catch (SecurityException ex) {
- // ignore
- }
- }
- }
-
- private RandomAccessFile miniThumbDataFile() {
- if (mMiniThumbFile == null) {
- removeOldFile();
- String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION);
- File directory = new File(path).getParentFile();
- if (!directory.isDirectory()) {
- if (!directory.mkdirs()) {
- Log.e(TAG, "Unable to create .thumbnails directory "
- + directory.toString());
- }
- }
- File f = new File(path);
- try {
- mMiniThumbFile = new RandomAccessFile(f, "rw");
- } catch (IOException ex) {
- // Open as read-only so we can at least read the existing
- // thumbnails.
- try {
- mMiniThumbFile = new RandomAccessFile(f, "r");
- } catch (IOException ex2) {
- // ignore exception
- }
- }
- if (mMiniThumbFile != null) {
- mChannel = mMiniThumbFile.getChannel();
- }
- }
- return mMiniThumbFile;
- }
-
- private MiniThumbFile(Uri uri) {
- mUri = uri;
- mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
- mEmptyBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
- }
-
- public synchronized void deactivate() {
- if (mMiniThumbFile != null) {
- try {
- mMiniThumbFile.close();
- mMiniThumbFile = null;
- } catch (IOException ex) {
- // ignore exception
- }
- }
- }
-
- // Get the magic number for the specified id in the mini-thumb file.
- // Returns 0 if the magic is not available.
- public synchronized long getMagic(long id) {
- // check the mini thumb file for the right data. Right is
- // defined as having the right magic number at the offset
- // reserved for this "id".
- RandomAccessFile r = miniThumbDataFile();
- if (r != null) {
- long pos = id * BYTES_PER_MINTHUMB;
- FileLock lock = null;
- try {
- mBuffer.clear();
- mBuffer.limit(1 + 8);
-
- lock = mChannel.lock(pos, 1 + 8, true);
- // check that we can read the following 9 bytes
- // (1 for the "status" and 8 for the long)
- if (mChannel.read(mBuffer, pos) == 9) {
- mBuffer.position(0);
- if (mBuffer.get() == 1) {
- return mBuffer.getLong();
- }
- }
- } catch (IOException ex) {
- Log.v(TAG, "Got exception checking file magic: ", ex);
- } catch (RuntimeException ex) {
- // Other NIO related exception like disk full, read only channel..etc
- Log.e(TAG, "Got exception when reading magic, id = " + id +
- ", disk full or mount read-only? " + ex.getClass());
- } finally {
- try {
- if (lock != null) lock.release();
- }
- catch (IOException ex) {
- // ignore it.
- }
- }
- }
- return 0;
- }
-
- public synchronized void eraseMiniThumb(long id) {
- RandomAccessFile r = miniThumbDataFile();
- if (r != null) {
- long pos = id * BYTES_PER_MINTHUMB;
- FileLock lock = null;
- try {
- mBuffer.clear();
- mBuffer.limit(1 + 8);
-
- lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);
- // check that we can read the following 9 bytes
- // (1 for the "status" and 8 for the long)
- if (mChannel.read(mBuffer, pos) == 9) {
- mBuffer.position(0);
- if (mBuffer.get() == 1) {
- long currentMagic = mBuffer.getLong();
- if (currentMagic == 0) {
- // there is no thumbnail stored here
- Log.i(TAG, "no thumbnail for id " + id);
- return;
- }
- // zero out the thumbnail slot
- // Log.v(TAG, "clearing slot " + id + ", magic " + currentMagic
- // + " at offset " + pos);
- mChannel.write(mEmptyBuffer, pos);
- }
- } else {
- // Log.v(TAG, "No slot");
- }
- } catch (IOException ex) {
- Log.v(TAG, "Got exception checking file magic: ", ex);
- } catch (RuntimeException ex) {
- // Other NIO related exception like disk full, read only channel..etc
- Log.e(TAG, "Got exception when reading magic, id = " + id +
- ", disk full or mount read-only? " + ex.getClass());
- } finally {
- try {
- if (lock != null) lock.release();
- }
- catch (IOException ex) {
- // ignore it.
- }
- }
- } else {
- // Log.v(TAG, "No data file");
- }
- }
-
- public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic)
- throws IOException {
- RandomAccessFile r = miniThumbDataFile();
- if (r == null) return;
-
- long pos = id * BYTES_PER_MINTHUMB;
- FileLock lock = null;
- try {
- if (data != null) {
- if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) {
- // not enough space to store it.
- return;
- }
- mBuffer.clear();
- mBuffer.put((byte) 1);
- mBuffer.putLong(magic);
- mBuffer.putInt(data.length);
- mBuffer.put(data);
- mBuffer.flip();
-
- lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);
- mChannel.write(mBuffer, pos);
- }
- } catch (IOException ex) {
- Log.e(TAG, "couldn't save mini thumbnail data for "
- + id + "; ", ex);
- throw ex;
- } catch (RuntimeException ex) {
- // Other NIO related exception like disk full, read only channel..etc
- Log.e(TAG, "couldn't save mini thumbnail data for "
- + id + "; disk full or mount read-only? " + ex.getClass());
- } finally {
- try {
- if (lock != null) lock.release();
- }
- catch (IOException ex) {
- // ignore it.
- }
- }
- }
-
- /**
- * Gallery app can use this method to retrieve mini-thumbnail. Full size
- * images share the same IDs with their corresponding thumbnails.
- *
- * @param id the ID of the image (same of full size image).
- * @param data the buffer to store mini-thumbnail.
- */
- public synchronized byte [] getMiniThumbFromFile(long id, byte [] data) {
- RandomAccessFile r = miniThumbDataFile();
- if (r == null) return null;
-
- long pos = id * BYTES_PER_MINTHUMB;
- FileLock lock = null;
- try {
- mBuffer.clear();
- lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true);
- int size = mChannel.read(mBuffer, pos);
- if (size > 1 + 8 + 4) { // flag, magic, length
- mBuffer.position(0);
- byte flag = mBuffer.get();
- long magic = mBuffer.getLong();
- int length = mBuffer.getInt();
-
- if (size >= 1 + 8 + 4 + length && length != 0 && magic != 0 && flag == 1 &&
- data.length >= length) {
- mBuffer.get(data, 0, length);
- return data;
- }
- }
- } catch (IOException ex) {
- Log.w(TAG, "got exception when reading thumbnail id=" + id + ", exception: " + ex);
- } catch (RuntimeException ex) {
- // Other NIO related exception like disk full, read only channel..etc
- Log.e(TAG, "Got exception when reading thumbnail, id = " + id +
- ", disk full or mount read-only? " + ex.getClass());
- } finally {
- try {
- if (lock != null) lock.release();
- }
- catch (IOException ex) {
- // ignore it.
- }
- }
- return null;
- }
-}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index fefb0d7..e207721 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -495,15 +495,17 @@
if (mCursor == null || !mCursor.moveToPosition(position)) {
return null;
}
-
- return getUriFromCursor(mCursor);
+
+ return getUriFromCursor(mContext, mCursor);
}
- private static Uri getUriFromCursor(Cursor cursor) {
- return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor
- .getLong(ID_COLUMN_INDEX));
+ private static Uri getUriFromCursor(Context context, Cursor cursor) {
+ final Uri uri = ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)),
+ cursor.getLong(ID_COLUMN_INDEX));
+ final Uri canonicalized = context.getContentResolver().canonicalize(uri);
+ return (canonicalized != null) ? canonicalized : uri;
}
-
+
/**
* Gets the position of a {@link Uri} within this {@link RingtoneManager}.
*
@@ -569,7 +571,7 @@
Uri uri = null;
if (cursor.moveToFirst()) {
- uri = getUriFromCursor(cursor);
+ uri = getUriFromCursor(context, cursor);
}
cursor.close();
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 1cd60f7..39474e1 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -416,6 +416,7 @@
* @param devices list of devices to which the audio stream of the application may be routed.
* @return true if the change was successful, false otherwise.
*/
+ @TestApi
@SystemApi
public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
if (devices == null) {
@@ -457,6 +458,7 @@
* @param uid UID of the application affected.
* @return true if the change was successful, false otherwise.
*/
+ @TestApi
@SystemApi
public boolean removeUidDeviceAffinity(int uid) {
synchronized (mLock) {
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 6361fd8..4ac6d35 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -36,6 +36,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Display;
import android.view.WindowManager;
@@ -69,8 +70,6 @@
private final Context mContext;
private final ContentProviderClient mMediaProvider;
- private final String mVolumeName;
- private final Uri mObjectsUri;
private final AtomicBoolean mClosed = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -78,10 +77,10 @@
private final HashMap<String, MtpStorage> mStorageMap = new HashMap<>();
// cached property groups for single properties
- private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty = new HashMap<>();
+ private final SparseArray<MtpPropertyGroup> mPropertyGroupsByProperty = new SparseArray<>();
// cached property groups for all properties for a given format
- private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat = new HashMap<>();
+ private final SparseArray<MtpPropertyGroup> mPropertyGroupsByFormat = new SparseArray<>();
// SharedPreferences for writable MTP device properties
private SharedPreferences mDeviceProperties;
@@ -271,14 +270,11 @@
}
};
- public MtpDatabase(Context context, String volumeName,
- String[] subDirectories) {
+ public MtpDatabase(Context context, String[] subDirectories) {
native_setup();
mContext = Objects.requireNonNull(context);
mMediaProvider = context.getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY);
- mVolumeName = volumeName;
- mObjectsUri = Files.getMtpObjectsUri(volumeName);
mManager = new MtpStorageManager(new MtpStorageManager.MtpNotifier() {
@Override
public void sendObjectAdded(int id) {
@@ -526,8 +522,7 @@
propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
final int[] propertyList = getSupportedObjectProperties(format);
- propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
- propertyList);
+ propertyGroup = new MtpPropertyGroup(propertyList);
mPropertyGroupsByFormat.put(format, propertyGroup);
}
} else {
@@ -535,12 +530,11 @@
propertyGroup = mPropertyGroupsByProperty.get(property);
if (propertyGroup == null) {
final int[] propertyList = new int[]{property};
- propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
- propertyList);
+ propertyGroup = new MtpPropertyGroup(propertyList);
mPropertyGroupsByProperty.put(property, propertyGroup);
}
}
- int err = propertyGroup.getPropertyList(obj, ret);
+ int err = propertyGroup.getPropertyList(mMediaProvider, obj.getVolumeName(), obj, ret);
if (err != MtpConstants.RESPONSE_OK) {
return new MtpPropertyList(err);
}
@@ -581,7 +575,8 @@
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs);
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+ mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
@@ -640,12 +635,12 @@
if (obj.getParent().isRoot()) {
values.put(Files.FileColumns.PARENT, 0);
} else {
- int parentId = findInMedia(path.getParent());
+ int parentId = findInMedia(newParentObj, path.getParent());
if (parentId != -1) {
values.put(Files.FileColumns.PARENT, parentId);
} else {
// The new parent isn't in MediaProvider, so delete the object instead
- deleteFromMedia(oldPath, obj.isDir());
+ deleteFromMedia(obj, oldPath, obj.isDir());
return;
}
}
@@ -655,13 +650,14 @@
try {
int parentId = -1;
if (!oldParentObj.isRoot()) {
- parentId = findInMedia(oldPath.getParent());
+ parentId = findInMedia(oldParentObj, oldPath.getParent());
}
if (oldParentObj.isRoot() || parentId != -1) {
// Old parent exists in MediaProvider - perform a move
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs);
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+ mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} else {
// Old parent doesn't exist - add the object
MediaStore.scanFile(mContext, path.toFile());
@@ -823,14 +819,16 @@
if (!mManager.endRemoveObject(obj, success))
Log.e(TAG, "Failed to end remove object");
if (success)
- deleteFromMedia(obj.getPath(), obj.isDir());
+ deleteFromMedia(obj, obj.getPath(), obj.isDir());
}
- private int findInMedia(Path path) {
+ private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+
int ret = -1;
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
+ c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE,
new String[]{path.toString()}, null, null);
if (c != null && c.moveToNext()) {
ret = c.getInt(0);
@@ -844,12 +842,13 @@
return ret;
}
- private void deleteFromMedia(Path path, boolean isDir) {
+ private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
try {
// Delete the object(s) from MediaProvider, but ignore errors.
if (isDir) {
// recursive case - delete all children first
- mMediaProvider.delete(mObjectsUri,
+ mMediaProvider.delete(objectsUri,
// the 'like' makes it use the index, the 'lower()' makes it correct
// when the path contains sqlite wildcard characters
"_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
@@ -858,7 +857,7 @@
}
String[] whereArgs = new String[]{path.toString()};
- if (mMediaProvider.delete(mObjectsUri, PATH_WHERE, whereArgs) > 0) {
+ if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
MediaStore.scanFile(mContext, path.getParent().toFile());
}
@@ -876,10 +875,10 @@
if (obj == null)
return null;
// Translate this handle to the MediaProvider Handle
- handle = findInMedia(obj.getPath());
+ handle = findInMedia(obj, obj.getPath());
if (handle == -1)
return null;
- Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
+ Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle);
Cursor c = null;
try {
c = mMediaProvider.query(uri, PATH_PROJECTION, null, null, null, null);
@@ -912,17 +911,17 @@
if (obj == null)
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
// Translate this handle to the MediaProvider Handle
- handle = findInMedia(obj.getPath());
+ handle = findInMedia(obj, obj.getPath());
if (handle == -1)
return MtpConstants.RESPONSE_GENERAL_ERROR;
- Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
+ Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle);
ArrayList<ContentValues> valuesList = new ArrayList<>();
for (int id : references) {
// Translate each reference id to the MediaProvider Id
MtpStorageManager.MtpObject refObj = mManager.getObject(id);
if (refObj == null)
continue;
- int refHandle = findInMedia(refObj.getPath());
+ int refHandle = findInMedia(refObj, refObj.getPath());
if (refHandle == -1)
continue;
ContentValues values = new ContentValues();
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index 6d5be8e..5bb0c1b 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -46,9 +46,6 @@
}
}
- private final ContentProviderClient mProvider;
- private final String mVolumeName;
-
// list of all properties in this group
private final Property[] mProperties;
@@ -58,10 +55,7 @@
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
// constructs a property group for a list of properties
- public MtpPropertyGroup(ContentProviderClient provider, String volumeName, int[] properties) {
- mProvider = provider;
- mVolumeName = volumeName;
-
+ public MtpPropertyGroup(int[] properties) {
int count = properties.length;
ArrayList<String> columns = new ArrayList<>(count);
columns.add(Files.FileColumns._ID);
@@ -175,7 +169,8 @@
* object and adds them to the given property list.
* @return Response_OK if the operation succeeded.
*/
- public int getPropertyList(MtpStorageManager.MtpObject object, MtpPropertyList list) {
+ public int getPropertyList(ContentProviderClient provider, String volumeName,
+ MtpStorageManager.MtpObject object, MtpPropertyList list) {
Cursor c = null;
int id = object.getId();
String path = object.getPath().toString();
@@ -184,8 +179,8 @@
try {
// Look up the entry in MediaProvider only if one of those properties is needed.
final Uri uri = MtpDatabase.getObjectPropertiesUri(object.getFormat(),
- mVolumeName);
- c = mProvider.query(uri, mColumns,
+ volumeName);
+ c = provider.query(uri, mColumns,
PATH_WHERE, new String[] {path}, null, null);
if (c != null && !c.moveToNext()) {
c.close();
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index c714b3c..65d0fef 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.os.storage.StorageVolume;
+import android.provider.MediaStore;
/**
* This class represents a storage unit on an MTP device.
@@ -27,12 +28,12 @@
* @hide
*/
public class MtpStorage {
-
private final int mStorageId;
private final String mPath;
private final String mDescription;
private final boolean mRemovable;
private final long mMaxFileSize;
+ private final String mVolumeName;
public MtpStorage(StorageVolume volume, int storageId) {
mStorageId = storageId;
@@ -40,6 +41,11 @@
mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
+ if (volume.isPrimary()) {
+ mVolumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
+ } else {
+ mVolumeName = volume.getNormalizedUuid();
+ }
}
/**
@@ -88,4 +94,8 @@
public long getMaxFileSize() {
return mMaxFileSize;
}
+
+ public String getVolumeName() {
+ return mVolumeName;
+ }
}
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index f14e7d7..e783788 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -21,6 +21,8 @@
import android.os.storage.StorageVolume;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
@@ -131,6 +133,7 @@
/** MtpObject represents either a file or directory in an associated storage. **/
public static class MtpObject {
+ private MtpStorage mStorage;
// null for root objects
private MtpObject mParent;
@@ -147,9 +150,10 @@
// null if not both a directory and visited
private FileObserver mObserver;
- MtpObject(String name, int id, MtpObject parent, boolean isDir) {
+ MtpObject(String name, int id, MtpStorage storage, MtpObject parent, boolean isDir) {
mId = id;
mName = name;
+ mStorage = Preconditions.checkNotNull(storage);
mParent = parent;
mObserver = null;
mVisited = false;
@@ -206,6 +210,10 @@
return mParent == null;
}
+ public String getVolumeName() {
+ return mStorage.getVolumeName();
+ }
+
/** For MtpStorageManager only **/
private void setName(String name) {
@@ -278,7 +286,7 @@
}
private MtpObject copy(boolean recursive) {
- MtpObject copy = new MtpObject(mName, mId, mParent, mIsDir);
+ MtpObject copy = new MtpObject(mName, mId, mStorage, mParent, mIsDir);
copy.mIsDir = mIsDir;
copy.mVisited = mVisited;
copy.mState = mState;
@@ -408,7 +416,7 @@
public synchronized MtpStorage addMtpStorage(StorageVolume volume) {
int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1;
MtpStorage storage = new MtpStorage(volume, storageId);
- MtpObject root = new MtpObject(storage.getPath(), storageId, null, true);
+ MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true);
mRoots.put(storageId, root);
return storage;
}
@@ -608,7 +616,7 @@
return null;
}
- MtpObject obj = new MtpObject(newName, getNextObjectId(), parent, isDir);
+ MtpObject obj = new MtpObject(newName, getNextObjectId(), parent.mStorage, parent, isDir);
mObjects.put(obj.getId(), obj);
parent.addChild(obj);
return obj;
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 55c9361..81c5bcd 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -52,6 +52,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.TrafficStatsConstants;
import java.io.IOException;
import java.lang.reflect.Field;
@@ -238,7 +239,8 @@
if (isFinishing() || isDestroyed()) return;
HttpURLConnection urlConnection = null;
int httpResponseCode = 500;
- int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+ int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_PROBE);
try {
urlConnection = (HttpURLConnection) mNetwork.openConnection(
new URL(mCm.getCaptivePortalServerUrl()));
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index b2baff5..7860f36 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -238,7 +238,7 @@
}
mSingleThreadExecutor.submit(() -> {
NotificationEntry entry =
- new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper);
+ new NotificationEntry(mPackageManager, sbn.cloneLight(), channel, mSmsHelper);
SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry);
if (DEBUG) {
Log.d(TAG, String.format(
@@ -296,7 +296,7 @@
Ranking ranking = getRanking(sbn.getKey(), rankingMap);
if (ranking != null && ranking.getChannel() != null) {
NotificationEntry entry = new NotificationEntry(mPackageManager,
- sbn, ranking.getChannel(), mSmsHelper);
+ sbn.cloneLight(), ranking.getChannel(), mSmsHelper);
String key = getKey(
sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 0d687d4..10360a3 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -338,7 +338,7 @@
createTextClassifierEventBuilder(
TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
.setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
- .setScore(session.repliesScores.getOrDefault(reply, 0f))
+ .setScores(session.repliesScores.getOrDefault(reply, 0f))
.build();
mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
@@ -381,11 +381,9 @@
.build();
}
- private TextClassifierEvent.Builder createTextClassifierEventBuilder(
+ private TextClassifierEvent.ConversationActionsEvent.Builder createTextClassifierEventBuilder(
int eventType, String resultId) {
- return new TextClassifierEvent.Builder(
- TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType)
- .setEventTime(System.currentTimeMillis())
+ return new TextClassifierEvent.ConversationActionsEvent.Builder(eventType)
.setEventContext(
new TextClassificationContext.Builder(
mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION)
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
index ebcaee8..dfa1ea0 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
@@ -330,7 +330,9 @@
List<TextClassifierEvent> events = argumentCaptor.getAllValues();
assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION);
- assertThat(events.get(1).getScore()).isEqualTo(SCORE);
+ float[] scores = events.get(1).getScores();
+ assertThat(scores).hasLength(1);
+ assertThat(scores[0]).isEqualTo(SCORE);
}
@Test
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
index 64adc0d..af0e3bb 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
@@ -65,6 +65,7 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.internal.util.TrafficStatsConstants;
import com.android.internal.util.WakeupMessage;
import java.io.FileDescriptor;
@@ -329,7 +330,8 @@
}
private boolean initUdpSocket() {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_DHCP);
try {
mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 8832eaa..d21b5f7 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -16,7 +16,6 @@
package android.net.dhcp;
-import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
@@ -33,6 +32,7 @@
import static android.system.OsConstants.SO_BROADCAST;
import static android.system.OsConstants.SO_REUSEADDR;
+import static com.android.internal.util.TrafficStatsConstants.TAG_SYSTEM_DHCP_SERVER;
import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 2a61250..27d4203 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -98,6 +98,7 @@
import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.internal.util.TrafficStatsConstants;
import com.android.networkstack.R;
import com.android.networkstack.metrics.DataStallDetectionStats;
import com.android.networkstack.metrics.DataStallStatsUtils;
@@ -1478,7 +1479,8 @@
int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
String redirectUrl = null;
final Stopwatch probeTimer = new Stopwatch().start();
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_PROBE);
try {
urlConnection = (HttpURLConnection) mNetwork.openConnection(url);
urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index 87b5b57..4f4aef0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -76,11 +76,13 @@
public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
int targetUserId = UserHandle.myUserId();
- if (admin != null && admin.user != null
- && isCurrentUserOrProfile(context, admin.user.getIdentifier())) {
- targetUserId = admin.user.getIdentifier();
+ if (admin != null) {
+ if (admin.user != null
+ && isCurrentUserOrProfile(context, admin.user.getIdentifier())) {
+ targetUserId = admin.user.getIdentifier();
+ }
+ intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction);
}
- intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction);
context.startActivityAsUser(intent, UserHandle.of(targetUserId));
}
diff --git a/packages/SettingsLib/res/layout/settings_dialog_title.xml b/packages/SettingsLib/res/layout/settings_dialog_title.xml
new file mode 100644
index 0000000..1e065e0
--- /dev/null
+++ b/packages/SettingsLib/res/layout/settings_dialog_title.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/settings_title_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/settings_title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:paddingTop="@*android:dimen/dialog_padding_top_material">
+
+ <ImageView
+ android:id="@+id/settings_icon"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_marginBottom="12dip" />
+
+ <TextView
+ android:id="@+id/settings_title"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ style="?android:attr/windowTitleStyle" />
+ </LinearLayout>
+
+ <Space
+ android:id="@+id/settings_title_divider"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@*android:dimen/dialog_title_divider_material" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
index eff02d2..257943e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
@@ -83,6 +83,8 @@
// Dual tone implies that battery level is a clipped overlay over top of the whole shape
private var dualTone = false
+ private var batteryLevel = 0
+
private val invalidateRunnable: () -> Unit = {
invalidateSelf()
}
@@ -177,9 +179,9 @@
unifiedPath.reset()
levelPath.reset()
levelRect.set(fillRect)
- val fillFraction = level / 100f
+ val fillFraction = batteryLevel / 100f
val fillTop =
- if (level >= 95)
+ if (batteryLevel >= 95)
fillRect.top
else
fillRect.top + (fillRect.height() * (1 - fillFraction))
@@ -223,7 +225,7 @@
fillPaint.color = levelColor
// Show colorError below this level
- if (level <= Companion.CRITICAL_LEVEL && !charging) {
+ if (batteryLevel <= Companion.CRITICAL_LEVEL && !charging) {
c.save()
c.clipPath(scaledFill)
c.drawPath(levelPath, fillPaint)
@@ -310,13 +312,13 @@
*/
public open fun setBatteryLevel(l: Int) {
invertFillIcon = if (l >= 67) true else if (l <= 33) false else invertFillIcon
- level = l
- levelColor = batteryColorForLevel(level)
+ batteryLevel = l
+ levelColor = batteryColorForLevel(batteryLevel)
invalidateSelf()
}
public fun getBatteryLevel(): Int {
- return level
+ return batteryLevel
}
override fun onBoundsChange(bounds: Rect?) {
@@ -343,7 +345,7 @@
dualToneBackgroundFill.color = bgColor
// Also update the level color, since fillColor may have changed
- levelColor = batteryColorForLevel(level)
+ levelColor = batteryColorForLevel(batteryLevel)
invalidateSelf()
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 1976ec4..d1e4fdf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -337,6 +337,7 @@
mContext = context;
networkId = config.networkId;
mConfig = config;
+ mFqdn = config.FQDN;
setScanResultsPasspoint(homeScans, roamingScans);
updateKey();
}
@@ -673,6 +674,13 @@
return mKey;
}
+ /**
+ * Determines if the other AccessPoint represents the same network as this AccessPoint
+ */
+ public boolean matches(AccessPoint other) {
+ return getKey().equals(other.getKey());
+ }
+
public boolean matches(WifiConfiguration config) {
if (config.isPasspoint()) {
return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 2b5a4e0..11c799e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -16,6 +16,7 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
@@ -28,11 +29,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.UserHandle;
@@ -42,6 +45,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -283,6 +287,26 @@
assertThat(profile).isNull();
}
+ @Test
+ public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() {
+ EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
+ enforcedAdmin.enforcedRestriction = "Dummy";
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
+ assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy");
+ }
+
+ @Test
+ public void sendShowAdminSupportDetailsIntent_noExtraRestriction() {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, null);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
+ assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isNull();
+ }
+
private UserInfo setUpUser(int userId, ComponentName[] admins) {
UserInfo userInfo = new UserInfo(userId, "primary", 0);
when(mUserManager.getUserInfo(userId)).thenReturn(userInfo);
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png
similarity index 100%
rename from packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
rename to packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
deleted file mode 100644
index 2bfd655..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable/analog_frame.xml b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml
new file mode 100644
index 0000000..a663ac8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="250dp"
+ android:width="250dp"
+ android:viewportHeight="380"
+ android:viewportWidth="380">
+ <path android:fillColor="#000000" android:pathData="M190,190m0,2a2,2 0,1 1,0 -4a2,2 0,1 1,0 4"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml
new file mode 100644
index 0000000..c7b6d60
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="250dp"
+ android:width="250dp"
+ android:viewportHeight="380"
+ android:viewportWidth="380">
+ <path android:fillColor="#777777" android:fillType="evenOdd" android:pathData="M203,190C203,185.398 200.608,181.354 197,179.044L197,58C197,54.134 193.866,51 190,51C186.134,51 183,54.134 183,58L183,179.043C179.392,181.354 177,185.397 177,190C177,197.18 182.82,203 190,203C197.18,203 203,197.18 203,190Z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml
new file mode 100644
index 0000000..458275b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="250dp"
+ android:width="250dp"
+ android:viewportHeight="380"
+ android:viewportWidth="380">
+ <path android:fillColor="#FFFFFF" android:pathData="M192,182.252C195.45,183.14 198,186.272 198,190C198,194.418 194.418,198 190,198C185.582,198 182,194.418 182,190C182,186.272 184.55,183.14 188,182.252L188,10C188,8.895 188.895,8 190,8C191.105,8 192,8.895 192,10L192,182.252Z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/analog_clock.xml
similarity index 67%
rename from packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml
rename to packages/SystemUI/res-keyguard/layout/analog_clock.xml
index dd25df8..cf6d35ee2 100644
--- a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml
+++ b/packages/SystemUI/res-keyguard/layout/analog_clock.xml
@@ -35,9 +35,27 @@
android:format24Hour="@string/keyguard_widget_24_hours_format"
android:elegantTextHeight="false"
/>
- <com.android.keyguard.clock.StretchAnalogClock
+ <com.android.keyguard.clock.ImageClock
android:id="@+id/analog_clock"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <ImageView
+ android:id="@+id/hour_hand"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/analog_hour_hand"
+ />
+ <ImageView
+ android:id="@+id/minute_hand"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/analog_minute_hand"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/analog_frame"
+ />
+ </com.android.keyguard.clock.ImageClock>
</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 4738887..1e98189 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -413,9 +413,6 @@
<string name="clock_title_bubble" translatable="false">Bubble</string>
<!-- Title for Stretch clock face that will appear in the picker app next to a preview image of
the clock face. [CHAR LIMIT=8] -->
- <string name="clock_title_stretch" translatable="false">Stretch</string>
- <!-- Title for Typographic clock face that will appear in the picker app next to a preview image of
- the clock face. [CHAR LIMIT=8] -->
- <string name="clock_title_type" translatable="false">Type</string>
+ <string name="clock_title_analog" translatable="false">Analog</string>
</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index e92ec0f..3cefce0 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -20,44 +20,50 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/global_actions_grid_item_width"
android:layout_height="@dimen/global_actions_grid_item_height"
- android:gravity="top|center_horizontal"
- android:orientation="vertical"
+ android:gravity="center"
android:layout_marginTop="@dimen/global_actions_grid_item_vertical_margin"
android:layout_marginBottom="@dimen/global_actions_grid_item_vertical_margin"
android:layout_marginLeft="@dimen/global_actions_grid_item_side_margin"
android:layout_marginRight="@dimen/global_actions_grid_item_side_margin"
>
- <ImageView
- android:id="@*android:id/icon"
- android:layout_width="@dimen/global_actions_grid_item_icon_width"
- android:layout_height="@dimen/global_actions_grid_item_icon_height"
- android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin"
- android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin"
- android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
- android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
- android:scaleType="centerInside"
- android:alpha="?android:attr/primaryContentAlpha"
- />
+ <LinearLayout
+ android:layout_width="@dimen/global_actions_grid_item_width"
+ android:layout_height="@dimen/global_actions_grid_item_height"
+ android:gravity="top|center_horizontal"
+ android:orientation="vertical"
+ >
+ <ImageView
+ android:id="@*android:id/icon"
+ android:layout_width="@dimen/global_actions_grid_item_icon_width"
+ android:layout_height="@dimen/global_actions_grid_item_icon_height"
+ android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin"
+ android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
+ android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
+ android:scaleType="centerInside"
+ android:alpha="?android:attr/primaryContentAlpha"
+ />
- <TextView
- android:id="@*android:id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:singleLine="true"
- android:gravity="center"
- android:textSize="12dp"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
+ <TextView
+ android:id="@*android:id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="12dp"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
- <TextView
- android:visibility="gone"
- android:id="@*android:id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textColor="?android:attr/textColorTertiary"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
+ <TextView
+ android:visibility="gone"
+ android:id="@*android:id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 444cabfc..f121c8e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1331,6 +1331,14 @@
<!-- Content description for accessibility: Clear the odi caption tool tip. [CHAR LIMIT=NONE] -->
<string name="accessibility_volume_close_odi_captions_tip">Close captions tip</string>
+ <!-- Content description for accessibility: Captions button. [CHAR LIMIT=NONE] -->
+ <string name="volume_odi_captions_content_description">Captions overlay</string>
+
+ <!-- Content description for accessibility: Hint if click will enable. [CHAR LIMIT=NONE] -->
+ <string name="volume_odi_captions_hint_enable">enable</string>
+ <!-- Content description for accessibility: Hint if click will disable. [CHAR LIMIT=NONE] -->
+ <string name="volume_odi_captions_hint_disable">disable</string>
+
<!-- content description for audio output chooser [CHAR LIMIT=NONE]-->
<string name="accessibility_output_chooser">Switch output device</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 64517ba..eca3926 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -16,14 +16,22 @@
package com.android.keyguard;
+import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
+import static android.telephony.PhoneStateListener.LISTEN_NONE;
+
+import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -36,6 +44,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -113,6 +122,17 @@
}
};
+ private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ mActiveMobileDataSubscription = subId;
+ if (mKeyguardUpdateMonitor != null) {
+ updateCarrierText();
+ }
+ }
+ };
+
/**
* The status of this lock screen. Primarily used for widgets on LockScreen.
*/
@@ -200,6 +220,8 @@
* @param callback Callback to provide text updates
*/
public void setListening(CarrierTextCallback callback) {
+ TelephonyManager telephonyManager = ((TelephonyManager) mContext
+ .getSystemService(Context.TELEPHONY_SERVICE));
if (callback != null) {
mCarrierTextCallback = callback;
if (ConnectivityManager.from(mContext).isNetworkSupported(
@@ -207,6 +229,8 @@
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mKeyguardUpdateMonitor.registerCallback(mCallback);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ telephonyManager.listen(mPhoneStateListener,
+ LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
} else {
// Don't listen and clear out the text when the device isn't a phone.
mKeyguardUpdateMonitor = null;
@@ -218,6 +242,35 @@
mKeyguardUpdateMonitor.removeCallback(mCallback);
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
}
+ telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
+ }
+ }
+
+ /**
+ * STOPSHIP(b/130246708) remove when no longer needed for testing purpose.
+ * @param subscriptions
+ */
+ private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
+ if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+ SubscriptionInfo info1 = subscriptions.get(0);
+ SubscriptionInfo info2 = subscriptions.get(1);
+ if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
+ // If both subscriptions are primary, show both.
+ if (!info1.isOpportunistic() && !info2.isOpportunistic()) return;
+
+ // If carrier required, always show signal bar of primary subscription.
+ // Otherwise, show whichever subscription is currently active for Internet.
+ boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
+ .getBoolean(CarrierConfigManager
+ .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
+ if (alwaysShowPrimary) {
+ subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
+ } else {
+ subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
+ ? info2 : info1);
+ }
+
+ }
}
}
@@ -226,7 +279,17 @@
boolean anySimReadyAndInService = false;
CharSequence displayText = null;
- List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+ // STOPSHIP(b/130246708) revert to mKeyguardUpdateMonitor.getSubscriptionInfo(false).
+ SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE));
+ List<SubscriptionInfo> subs = subscriptionManager.getActiveSubscriptionInfoList(false);
+
+ if (subs == null) {
+ subs = new ArrayList<>();
+ } else {
+ filterMobileSubscriptionInSameGroup(subs);
+ }
+
final int numSubs = subs.size();
final int[] subsIds = new int[numSubs];
// This array will contain in position i, the index of subscription in slot ID i.
@@ -311,20 +374,23 @@
displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot,
allSimsMissing);
+ boolean airplaneMode = false;
// APM (airplane mode) != no carrier state. There are carrier services
// (e.g. WFC = Wi-Fi calling) which may operate in APM.
if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
displayText = getAirplaneModeMessage();
+ airplaneMode = true;
}
- if (TextUtils.isEmpty(displayText)) {
+ if (TextUtils.isEmpty(displayText) && !airplaneMode) {
displayText = TextUtils.join(mSeparator, carrierNames);
}
final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
displayText,
carrierNames,
!allSimsMissing,
- subsIds);
+ subsIds,
+ airplaneMode);
postToCallback(info);
}
@@ -525,14 +591,22 @@
public final CharSequence[] listOfCarriers;
public final boolean anySimReady;
public final int[] subscriptionIds;
+ public boolean airplaneMode;
@VisibleForTesting
public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
boolean anySimReady, int[] subscriptionIds) {
+ this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
+ }
+
+ @VisibleForTesting
+ public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
+ boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
this.carrierText = carrierText;
this.listOfCarriers = listOfCarriers;
this.anySimReady = anySimReady;
this.subscriptionIds = subscriptionIds;
+ this.airplaneMode = airplaneMode;
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
similarity index 90%
rename from packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
rename to packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
index 81b6a60..1652121 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
@@ -35,7 +35,7 @@
/**
* Controller for Stretch clock that can appear on lock screen and AOD.
*/
-public class StretchAnalogClockController implements ClockPlugin {
+public class AnalogClockController implements ClockPlugin {
/**
* Resources used to get title and thumbnail.
@@ -60,9 +60,9 @@
/**
* Custom clock shown on AOD screen and behind stack scroller on lock.
*/
- private View mBigClockView;
+ private ClockLayout mBigClockView;
private TextClock mDigitalClock;
- private StretchAnalogClock mAnalogClock;
+ private ImageClock mAnalogClock;
/**
* Small clock shown on lock screen above stack scroller.
@@ -82,7 +82,7 @@
* @param inflater Inflater used to inflate custom clock views.
* @param colorExtractor Extracts accent color from wallpaper.
*/
- public StretchAnalogClockController(Resources res, LayoutInflater inflater,
+ public AnalogClockController(Resources res, LayoutInflater inflater,
SysuiColorExtractor colorExtractor) {
mResources = res;
mLayoutInflater = inflater;
@@ -90,7 +90,7 @@
}
private void createViews() {
- mBigClockView = mLayoutInflater.inflate(R.layout.stretchanalog_clock, null);
+ mBigClockView = (ClockLayout) mLayoutInflater.inflate(R.layout.analog_clock, null);
mAnalogClock = mBigClockView.findViewById(R.id.analog_clock);
mDigitalClock = mBigClockView.findViewById(R.id.digital_clock);
@@ -114,17 +114,17 @@
@Override
public String getName() {
- return "stretch";
+ return "analog";
}
@Override
public String getTitle() {
- return mResources.getString(R.string.clock_title_stretch);
+ return mResources.getString(R.string.clock_title_analog);
}
@Override
public Bitmap getThumbnail() {
- return BitmapFactory.decodeResource(mResources, R.drawable.stretch_thumbnail);
+ return BitmapFactory.decodeResource(mResources, R.drawable.analog_thumbnail);
}
@Override
@@ -175,13 +175,14 @@
}
final int length = colorPalette.length;
mDigitalClock.setTextColor(colorPalette[Math.max(0, length - 5)]);
- mAnalogClock.setClockColor(colorPalette[Math.max(0, length - 5)],
+ mAnalogClock.setClockColors(colorPalette[Math.max(0, length - 5)],
colorPalette[Math.max(0, length - 2)]);
}
@Override
public void onTimeTick() {
mAnalogClock.onTimeChanged();
+ mBigClockView.onTimeChanged();
mDigitalClock.refresh();
mLockClock.refresh();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index d30f45f..6069a5e 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -60,7 +60,7 @@
/**
* Custom clock shown on AOD screen and behind stack scroller on lock.
*/
- private View mView;
+ private ClockLayout mView;
private TextClock mDigitalClock;
private ImageClock mAnalogClock;
@@ -90,7 +90,7 @@
}
private void createViews() {
- mView = mLayoutInflater.inflate(R.layout.bubble_clock, null);
+ mView = (ClockLayout) mLayoutInflater.inflate(R.layout.bubble_clock, null);
mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock);
mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock);
@@ -186,6 +186,7 @@
@Override
public void onTimeTick() {
mAnalogClock.onTimeChanged();
+ mView.onTimeChanged();
mDigitalClock.refresh();
mLockClock.refresh();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
index 7ffee5d..55088a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -32,6 +32,7 @@
*/
public class ClockLayout extends FrameLayout {
+ private static final int ANALOG_CLOCK_SHIFT_FACTOR = 3;
/**
* Clock face views.
*/
@@ -73,7 +74,14 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ positionChildren();
+ }
+ void onTimeChanged() {
+ positionChildren();
+ }
+
+ private void positionChildren() {
final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX * 2, true)
- mBurnInPreventionOffsetX;
final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY * 2, false)
@@ -89,9 +97,9 @@
// Put the analog clock in the middle of the screen.
if (mAnalogClock != null) {
mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth()))
- + offsetX);
+ + ANALOG_CLOCK_SHIFT_FACTOR * offsetX);
mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
- + offsetY);
+ + ANALOG_CLOCK_SHIFT_FACTOR * offsetY);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index e373ca1..06488b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -142,8 +142,7 @@
addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor));
addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor));
- addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater,
- colorExtractor));
+ addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor));
// Store the size of the display for generation of clock preview.
DisplayMetrics dm = res.getDisplayMetrics();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
deleted file mode 100644
index 8cdd632..0000000
--- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2019 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.keyguard.clock;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * Analog clock where the minute hand extends off of the screen.
- */
-public class StretchAnalogClock extends View {
-
- private static final int DEFAULT_COLOR = Color.parseColor("#F5C983");
- private static final float HOUR_STROKE_WIDTH = 60f;
- private static final float MINUTE_STROKE_WIDTH = 20f;
- private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 80f;
-
- private final Paint mHourPaint = new Paint();
- private final Paint mMinutePaint = new Paint();
- private Calendar mTime = Calendar.getInstance(TimeZone.getDefault());
- private TimeZone mTimeZone;
-
- public StretchAnalogClock(Context context) {
- this(context, null);
- }
-
- public StretchAnalogClock(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
-
- /**
- * Call when the time changes to update the clock hands.
- */
- public void onTimeChanged() {
- mTime.setTimeInMillis(System.currentTimeMillis());
- invalidate();
- }
-
- /**
- * Call when the time zone has changed to update clock hands.
- *
- * @param timeZone The updated time zone that will be used.
- */
- public void onTimeZoneChanged(TimeZone timeZone) {
- mTime.setTimeZone(timeZone);
- }
-
- /**
- * Set the colors to use on the clock face.
- * @param dark Darker color obtained from color palette.
- * @param light Lighter color obtained from color palette.
- */
- public void setClockColor(int dark, int light) {
- mHourPaint.setColor(dark);
- invalidate();
- }
-
- private void init() {
- mHourPaint.setColor(DEFAULT_COLOR);
- mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
- mHourPaint.setAntiAlias(true);
- mHourPaint.setStrokeCap(Paint.Cap.ROUND);
-
- mMinutePaint.setColor(Color.WHITE);
- mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
- mMinutePaint.setAntiAlias(true);
- mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- final float centerX = getWidth() / 2f;
- final float centerY = getHeight() / 2f;
-
- final float minutesRotation = mTime.get(Calendar.MINUTE) * 6f;
- final float hoursRotation = mTime.get(Calendar.HOUR) * 30
- + mTime.get(Calendar.MINUTE) * 0.5f;
-
- // Compute length of clock hands. Hour hand is 60% the length from center to edge
- // and minute hand is twice the length to make sure it extends past screen edge.
- double sMinuteHandLengthFactor = Math.sin(2d * Math.PI * minutesRotation / 360d);
- float sMinuteHandLength = (float) (2d * (centerY + (centerX - centerY)
- * sMinuteHandLengthFactor * sMinuteHandLengthFactor));
- double sHourHandLengthFactor = Math.sin(2d * Math.PI * hoursRotation / 360d);
- float sHourHandLength = (float) (0.6d * (centerY + (centerX - centerY)
- * sHourHandLengthFactor * sHourHandLengthFactor));
-
- canvas.save();
-
- canvas.rotate(minutesRotation, centerX, centerY);
- canvas.drawLine(
- centerX,
- centerY + CENTER_GAP_AND_CIRCLE_RADIUS,
- centerX,
- centerY - sMinuteHandLength,
- mMinutePaint);
-
- canvas.rotate(hoursRotation - minutesRotation, centerX, centerY);
- canvas.drawLine(
- centerX,
- centerY + CENTER_GAP_AND_CIRCLE_RADIUS,
- centerX,
- centerY - sHourHandLength,
- mHourPaint);
-
- canvas.restore();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
- onTimeChanged();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 665df77..0332477 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,47 +16,65 @@
package com.android.systemui.bubbles;
+import android.os.UserHandle;
import android.view.LayoutInflater;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.util.Objects;
+
/**
* Encapsulates the data and UI elements of a bubble.
*/
class Bubble {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "Bubble";
+
private final String mKey;
+ private final String mGroupId;
private final BubbleExpandedView.OnBubbleBlockedListener mListener;
private boolean mInflated;
-
- public BubbleView iconView;
- public BubbleExpandedView expandedView;
public NotificationEntry entry;
+ BubbleView iconView;
+ BubbleExpandedView expandedView;
+
+ private static String groupId(NotificationEntry entry) {
+ UserHandle user = entry.notification.getUser();
+ return user.getIdentifier() + '|' + entry.notification.getPackageName();
+ }
Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
entry = e;
mKey = e.key;
+ mGroupId = groupId(e);
mListener = listener;
}
- /** @deprecated use the other constructor to defer View creation. */
- @Deprecated
- Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView,
- BubbleExpandedView.OnBubbleBlockedListener listener) {
- this(e, listener);
- inflate(inflater, stackView);
- }
-
public String getKey() {
return mKey;
}
+ public String getGroupId() {
+ return mGroupId;
+ }
+
+ public String getPackageName() {
+ return entry.notification.getPackageName();
+ }
+
boolean isInflated() {
return mInflated;
}
+ public void updateDotVisibility() {
+ if (iconView != null) {
+ iconView.updateDotVisibility();
+ }
+ }
+
void inflate(LayoutInflater inflater, BubbleStackView stackView) {
if (mInflated) {
return;
@@ -73,10 +91,32 @@
mInflated = true;
}
+ void setDismissed() {
+ entry.setBubbleDismissed(true);
+ // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker
+ if (expandedView != null) {
+ expandedView.cleanUpExpandedState();
+ }
+ }
+
void setEntry(NotificationEntry entry) {
+ this.entry = entry;
if (mInflated) {
iconView.update(entry);
expandedView.update(entry);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Bubble)) return false;
+ Bubble bubble = (Bubble) o;
+ return Objects.equals(mKey, bubble.mKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mKey);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index af9fd1d..6d0141b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -78,8 +78,7 @@
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
@Singleton
-public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener,
- ConfigurationController.ConfigurationListener {
+public class BubbleController implements ConfigurationController.ConfigurationListener {
private static final String TAG = "BubbleController";
@@ -178,6 +177,10 @@
@Override
public void onStateChanged(int newState) {
mState = newState;
+ boolean shouldCollapse = (mState != SHADE);
+ if (shouldCollapse) {
+ collapseStack();
+ }
updateVisibility();
}
}
@@ -240,7 +243,6 @@
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
- mStackView.setOnBlockedListener(this);
}
}
@@ -296,28 +298,38 @@
if (mStackView == null) {
return false;
}
- for (Bubble bubble : mBubbleData.getBubbles()) {
- if (!bubble.entry.isBubbleDismissed()) {
- return true;
- }
- }
- return false;
+ return mBubbleData.hasBubbles();
}
/**
* Whether the stack of bubbles is expanded or not.
*/
public boolean isStackExpanded() {
- return mStackView != null && mStackView.isExpanded();
+ return mBubbleData.isExpanded();
+ }
+
+ /**
+ * Tell the stack of bubbles to expand.
+ */
+ public void expandStack() {
+ mBubbleData.setExpanded(true);
}
/**
* Tell the stack of bubbles to collapse.
*/
public void collapseStack() {
- if (mStackView != null) {
- mStackView.collapseStack();
- }
+ mBubbleData.setExpanded(false /* expanded */);
+ }
+
+ void selectBubble(Bubble bubble) {
+ mBubbleData.setSelectedBubble(bubble);
+ }
+
+ @VisibleForTesting
+ void selectBubble(String key) {
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ selectBubble(bubble);
}
/**
@@ -326,8 +338,10 @@
* @param notificationKey the notification key for the bubble to be selected
*/
public void expandStackAndSelectBubble(String notificationKey) {
- if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) {
- mStackView.setExpandedBubble(notificationKey);
+ Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
+ if (bubble != null) {
+ mBubbleData.setSelectedBubble(bubble);
+ mBubbleData.setExpanded(true);
}
}
@@ -335,13 +349,7 @@
* Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
*/
void dismissStack(@DismissReason int reason) {
- if (mStackView == null) {
- return;
- }
- mStackView.stackDismissed(reason);
-
- updateVisibility();
- mNotificationEntryManager.updateNotifications();
+ mBubbleData.dismissAll(reason);
}
/**
@@ -360,20 +368,7 @@
* @param notif the notification associated with this bubble.
*/
void updateBubble(NotificationEntry notif) {
- if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
- // It's an update
- mStackView.updateBubble(notif);
- } else {
- // It's new
- ensureStackViewCreated();
- mStackView.addBubble(notif);
- }
- Bubble bubble = mBubbleData.getBubble(notif.key);
- if (shouldAutoExpand(notif)) {
- mStackView.setSelectedBubble(bubble);
- mStackView.setExpanded(true);
- }
- updateVisibility();
+ mBubbleData.notificationEntryUpdated(notif);
}
/**
@@ -383,23 +378,10 @@
*/
@MainThread
void removeBubble(String key, int reason) {
- if (mStackView != null) {
- mStackView.removeBubble(key, reason);
- }
- mNotificationEntryManager.updateNotifications();
- updateVisibility();
- }
-
- @Override
- public void onBubbleBlocked(NotificationEntry entry) {
- Object[] bubbles = mBubbleData.getBubbles().toArray();
- for (int i = 0; i < bubbles.length; i++) {
- NotificationEntry e = ((Bubble) bubbles[i]).entry;
- boolean samePackage = entry.notification.getPackageName().equals(
- e.notification.getPackageName());
- if (samePackage) {
- removeBubble(entry.key, DISMISS_BLOCKED);
- }
+ // TEMP: refactor to change this to pass entry
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ if (bubble != null) {
+ mBubbleData.notificationEntryRemoved(bubble.entry, reason);
}
}
@@ -436,7 +418,6 @@
updateShowInShadeForSuppressNotification(entry);
entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
updateBubble(entry);
- mStackView.updateDotVisibility(entry.key);
}
}
@@ -458,44 +439,57 @@
}
};
+ @SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
+
@Override
public void onBubbleAdded(Bubble bubble) {
-
+ ensureStackViewCreated();
+ mStackView.addBubble(bubble);
}
@Override
public void onBubbleRemoved(Bubble bubble, int reason) {
-
+ if (mStackView != null) {
+ mStackView.removeBubble(bubble);
+ }
}
public void onBubbleUpdated(Bubble bubble) {
-
+ if (mStackView != null) {
+ mStackView.updateBubble(bubble);
+ }
}
@Override
public void onOrderChanged(List<Bubble> bubbles) {
-
}
@Override
public void onSelectionChanged(Bubble selectedBubble) {
-
+ if (mStackView != null) {
+ mStackView.setSelectedBubble(selectedBubble);
+ }
}
@Override
public void onExpandedChanged(boolean expanded) {
-
+ if (mStackView != null) {
+ mStackView.setExpanded(expanded);
+ }
}
@Override
public void showFlyoutText(Bubble bubble, String text) {
-
+ if (mStackView != null) {
+ mStackView.animateInFlyoutForBubble(bubble);
+ }
}
@Override
public void apply() {
-
+ mNotificationEntryManager.updateNotifications();
+ updateVisibility();
}
};
@@ -526,7 +520,6 @@
mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
} else if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
- collapseStack();
}
updateBubblesShowing();
}
@@ -633,14 +626,14 @@
@Override
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
- mStackView.collapseStack();
+ mBubbleData.setExpanded(false);
}
}
@Override
public void onActivityLaunchOnSecondaryDisplayRerouted() {
if (mStackView != null) {
- mStackView.collapseStack();
+ mBubbleData.setExpanded(false);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index fe3f9d1..259665d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -15,14 +15,24 @@
*/
package com.android.systemui.bubbles;
-import androidx.annotation.Nullable;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -33,6 +43,8 @@
@Singleton
public class BubbleData {
+ private static final String TAG = "BubbleData";
+
/**
* This interface reports changes to the state and appearance of bubbles which should be applied
* as necessary to the UI.
@@ -53,7 +65,7 @@
* A Bubble has been removed. A call to {@link #onOrderChanged(List)} will
* follow.
*/
- void onBubbleRemoved(Bubble bubble, @BubbleController.DismissReason int reason);
+ void onBubbleRemoved(Bubble bubble, @DismissReason int reason);
/**
* An existing bubble has been updated.
@@ -86,46 +98,253 @@
void apply();
}
- private HashMap<String, Bubble> mBubbles = new HashMap<>();
+ private final Context mContext;
+ private final List<Bubble> mBubbles = new ArrayList<>();
+ private Bubble mSelectedBubble;
+ private boolean mExpanded;
private Listener mListener;
@VisibleForTesting
@Inject
- public BubbleData() {}
-
- /**
- * The set of bubbles.
- */
- public Collection<Bubble> getBubbles() {
- return mBubbles.values();
+ public BubbleData(Context context) {
+ mContext = context;
}
- @Nullable
- public Bubble getBubble(String key) {
- return mBubbles.get(key);
+ public boolean hasBubbles() {
+ return !mBubbles.isEmpty();
}
- public void addBubble(Bubble b) {
- mBubbles.put(b.getKey(), b);
+ public boolean isExpanded() {
+ return mExpanded;
}
- @Nullable
- public Bubble removeBubble(String key) {
- return mBubbles.remove(key);
+ public boolean hasBubbleWithKey(String key) {
+ return getBubbleWithKey(key) != null;
}
- public void updateBubble(String key, NotificationEntry newEntry) {
- Bubble oldBubble = mBubbles.get(key);
- if (oldBubble != null) {
- oldBubble.setEntry(newEntry);
+ public void setExpanded(boolean expanded) {
+ if (setExpandedInternal(expanded)) {
+ mListener.apply();
}
}
- public void clear() {
- mBubbles.clear();
+ public void setSelectedBubble(Bubble bubble) {
+ if (setSelectedBubbleInternal(bubble)) {
+ mListener.apply();
+ }
+ }
+
+ public void notificationEntryUpdated(NotificationEntry entry) {
+ Bubble bubble = getBubbleWithKey(entry.key);
+ if (bubble == null) {
+ // Create a new bubble
+ bubble = new Bubble(entry, this::onBubbleBlocked);
+ mBubbles.add(0, bubble); // TODO: reorder/group
+ mListener.onBubbleAdded(bubble);
+ } else {
+ // Updates an existing bubble
+ bubble.setEntry(entry);
+ mListener.onBubbleUpdated(bubble);
+ }
+ if (shouldAutoExpand(entry)) {
+ setSelectedBubbleInternal(bubble);
+ if (!mExpanded) {
+ setExpandedInternal(true);
+ }
+ } else if (mSelectedBubble == null) {
+ setSelectedBubbleInternal(bubble);
+ }
+ // TODO: reorder/group
+ mListener.apply();
+ }
+
+ public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
+ int indexToRemove = indexForKey(entry.key);
+ if (indexToRemove >= 0) {
+ Bubble removed = mBubbles.remove(indexToRemove);
+ removed.setDismissed();
+ mListener.onBubbleRemoved(removed, reason);
+ maybeSendDeleteIntent(reason, removed.entry);
+
+ if (mBubbles.isEmpty()) {
+ setExpandedInternal(false);
+ setSelectedBubbleInternal(null);
+ } else if (removed == mSelectedBubble) {
+ int newIndex = Math.min(indexToRemove, mBubbles.size() - 1);
+ Bubble newSelected = mBubbles.get(newIndex);
+ setSelectedBubbleInternal(newSelected);
+ }
+ // TODO: reorder/group
+ mListener.apply();
+ }
+ }
+
+ public void dismissAll(@DismissReason int reason) {
+ boolean changed = setExpandedInternal(false);
+ while (!mBubbles.isEmpty()) {
+ Bubble bubble = mBubbles.remove(0);
+ bubble.setDismissed();
+ maybeSendDeleteIntent(reason, bubble.entry);
+ mListener.onBubbleRemoved(bubble, reason);
+ changed = true;
+ }
+ if (setSelectedBubbleInternal(null)) {
+ changed = true;
+ }
+ if (changed) {
+ // TODO: reorder/group
+ mListener.apply();
+ }
+ }
+
+ /**
+ * Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if
+ * the value changes.
+ *
+ * @param bubble the new selected bubble
+ * @return true if the state changed as a result
+ */
+ private boolean setSelectedBubbleInternal(Bubble bubble) {
+ if (Objects.equals(bubble, mSelectedBubble)) {
+ return false;
+ }
+ if (bubble != null && !mBubbles.contains(bubble)) {
+ Log.e(TAG, "Cannot select bubble which doesn't exist!"
+ + " (" + bubble + ") bubbles=" + mBubbles);
+ return false;
+ }
+ if (mExpanded) {
+ // TODO: bubble.markAsActive() ?
+ bubble.entry.setShowInShadeWhenBubble(false);
+ }
+ mSelectedBubble = bubble;
+ mListener.onSelectionChanged(mSelectedBubble);
+ return true;
+ }
+
+
+ /**
+ * Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if
+ * the value changes.
+ *
+ * @param shouldExpand the new requested state
+ * @return true if the state changed as a result
+ */
+ private boolean setExpandedInternal(boolean shouldExpand) {
+ if (mExpanded == shouldExpand) {
+ return false;
+ }
+ if (shouldExpand) {
+ if (mBubbles.isEmpty()) {
+ Log.e(TAG, "Attempt to expand stack when empty!");
+ return false;
+ }
+ if (mSelectedBubble == null) {
+ Log.e(TAG, "Attempt to expand stack without selected bubble!");
+ return false;
+ }
+ // TODO: bubble.markAsActive() ?
+ mSelectedBubble.entry.setShowInShadeWhenBubble(false);
+ }
+ // TODO: reorder/regroup
+ mExpanded = shouldExpand;
+ mListener.onExpandedChanged(mExpanded);
+ return true;
+ }
+
+ private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
+ if (reason == BubbleController.DISMISS_USER_GESTURE) {
+ Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
+ PendingIntent deleteIntent = bubbleMetadata != null
+ ? bubbleMetadata.getDeleteIntent()
+ : null;
+ if (deleteIntent != null) {
+ try {
+ deleteIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key);
+ }
+ }
+ }
+ }
+
+ private void onBubbleBlocked(NotificationEntry entry) {
+ boolean changed = false;
+ final String blockedPackage = entry.notification.getPackageName();
+ for (Iterator<Bubble> i = mBubbles.iterator(); i.hasNext(); ) {
+ Bubble bubble = i.next();
+ if (bubble.getPackageName().equals(blockedPackage)) {
+ i.remove();
+ mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
+ changed = true;
+ }
+ }
+ if (changed) {
+ // TODO: reorder/group
+ mListener.apply();
+ }
+ }
+
+ private int indexForKey(String key) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private Bubble removeBubbleWithKey(String key) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ mBubbles.remove(i);
+ return bubble;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The set of bubbles.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public Collection<Bubble> getBubbles() {
+ return Collections.unmodifiableList(mBubbles);
+ }
+
+ @VisibleForTesting(visibility = PRIVATE)
+ Bubble getBubbleWithKey(String key) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ return bubble;
+ }
+ }
+ return null;
}
public void setListener(Listener listener) {
mListener = listener;
}
-}
+
+ boolean shouldAutoExpand(NotificationEntry entry) {
+ Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
+ return metadata != null && metadata.getAutoExpandBubble()
+ && isForegroundApp(entry.notification.getPackageName());
+ }
+
+ /**
+ * Return true if the applications with the package name is running in foreground.
+ *
+ * @param pkgName application package name.
+ */
+ boolean isForegroundApp(String pkgName) {
+ ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */);
+ return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 024e564..a4a0fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -20,8 +20,6 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
@@ -54,9 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
@@ -390,14 +386,13 @@
}
switch (action) {
case AccessibilityNodeInfo.ACTION_DISMISS:
- Dependency.get(BubbleController.class).dismissStack(
- BubbleController.DISMISS_ACCESSIBILITY_ACTION);
+ mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
return true;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
- collapseStack();
+ mBubbleData.setExpanded(false);
return true;
case AccessibilityNodeInfo.ACTION_EXPAND:
- expandStack();
+ mBubbleData.setExpanded(true);
return true;
}
return false;
@@ -424,9 +419,9 @@
* @param key the {@link NotificationEntry#key} associated with the bubble.
*/
public void updateDotVisibility(String key) {
- Bubble b = mBubbleData.getBubble(key);
+ Bubble b = mBubbleData.getBubbleWithKey(key);
if (b != null) {
- b.iconView.updateDotVisibility();
+ b.updateDotVisibility();
}
}
@@ -438,16 +433,6 @@
}
/**
- * Sets the listener to notify when a bubble is blocked.
- */
- public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) {
- mBlockedListener = listener;
- for (Bubble b : mBubbleData.getBubbles()) {
- b.expandedView.setOnBlockedListener(mBlockedListener);
- }
- }
-
- /**
* Whether the stack of bubbles is expanded or not.
*/
public boolean isExpanded() {
@@ -476,7 +461,7 @@
*/
@Deprecated
void setExpandedBubble(String key) {
- Bubble bubbleToExpand = mBubbleData.getBubble(key);
+ Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key);
if (bubbleToExpand != null) {
setSelectedBubble(bubbleToExpand);
bubbleToExpand.entry.setShowInShadeWhenBubble(false);
@@ -497,11 +482,36 @@
}
}
+ // via BubbleData.Listener
+ void addBubble(Bubble bubble) {
+ bubble.inflate(mInflater, this);
+ mBubbleContainer.addView(bubble.iconView, 0,
+ new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters);
+ requestUpdate();
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+ }
+
+ // via BubbleData.Listener
+ void removeBubble(Bubble bubble) {
+ // Remove it from the views
+ int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
+ mBubbleContainer.removeViewAt(removedIndex);
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ }
+
+ // via BubbleData.Listener
+ void updateBubble(Bubble bubble) {
+ requestUpdate();
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+ }
+
/**
* Changes the currently selected bubble. If the stack is already expanded, the newly selected
* bubble will be shown immediately. This does not change the expanded state or change the
* position of any bubble.
*/
+ // via BubbleData.Listener
public void setSelectedBubble(Bubble bubbleToSelect) {
if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
return;
@@ -520,7 +530,8 @@
logBubbleEvent(previouslySelected, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
logBubbleEvent(bubbleToSelect, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
notifyExpansionChanged(previouslySelected.entry, false /* expanded */);
- notifyExpansionChanged(bubbleToSelect.entry, true /* expanded */);
+ notifyExpansionChanged(bubbleToSelect == null ? null : bubbleToSelect.entry,
+ true /* expanded */);
});
}
}
@@ -528,13 +539,15 @@
/**
* Changes the expanded state of the stack.
*
- * @param expanded whether the bubble stack should appear expanded
+ * @param shouldExpand whether the bubble stack should appear expanded
*/
- public void setExpanded(boolean expanded) {
- if (expanded == mIsExpanded) {
+ // via BubbleData.Listener
+ public void setExpanded(boolean shouldExpand) {
+ boolean wasExpanded = mIsExpanded;
+ if (shouldExpand == wasExpanded) {
return;
}
- if (mIsExpanded) {
+ if (wasExpanded) {
// Collapse the stack
animateExpansion(false /* expand */);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
@@ -549,131 +562,17 @@
}
/**
- * Adds a bubble to the top of the stack.
- *
- * @param entry the notification to add to the stack of bubbles.
- */
- void addBubble(NotificationEntry entry) {
- Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
- mBubbleData.addBubble(b);
-
- mBubbleContainer.addView(b.iconView, 0,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters);
-
- requestUpdate();
- logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
-
- animateInFlyoutForBubble(b);
- }
-
- /**
- * Remove a bubble from the stack.
- */
- void removeBubble(String key, int reason) {
- Bubble b = mBubbleData.removeBubble(key);
- if (b == null) {
- return;
- }
- setBubbleDismissed(b, reason);
-
- // Remove it from the views
- int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
- mBubbleContainer.removeViewAt(removedIndex);
-
- int bubbleCount = mBubbleContainer.getChildCount();
- if (bubbleCount == 0) {
- // If no bubbles remain, collapse the entire stack.
- collapseStack();
- return;
- } else if (b.equals(mExpandedBubble)) {
- // Was the current bubble just removed?
- // If we have other bubbles and are expanded go to the next one or previous
- // if the bubble removed was last
- int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
- BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
- if (mIsExpanded) {
- setExpandedBubble(expandedBubble.getKey());
- } else {
- mExpandedBubble = null;
- }
- }
- // TODO: consider logging reason code
- logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
- }
-
- /**
* Dismiss the stack of bubbles.
+ * @deprecated
*/
+ @Deprecated
void stackDismissed(int reason) {
- for (Bubble bubble : mBubbleData.getBubbles()) {
- setBubbleDismissed(bubble, reason);
- }
- mBubbleData.clear();
- collapseStack();
- mBubbleContainer.removeAllViews();
- mExpandedViewContainer.removeAllViews();
- // TODO: consider logging reason code
+ mBubbleData.dismissAll(reason);
logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
}
/**
- * Marks the notification entry as dismissed & calls any delete intents for the bubble.
- *
- * <p>Note: This does not remove the Bubble from BubbleData.
- *
- * @param bubble the Bubble being dismissed
- * @param reason code for the reason the dismiss was triggered
- * @see BubbleController.DismissReason
- */
- private void setBubbleDismissed(Bubble bubble, @DismissReason int reason) {
- if (DEBUG) {
- Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason);
- }
- bubble.entry.setBubbleDismissed(true);
- bubble.expandedView.cleanUpExpandedState();
-
- if (reason == BubbleController.DISMISS_USER_GESTURE) {
- Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata();
- PendingIntent deleteIntent = bubbleMetadata != null
- ? bubbleMetadata.getDeleteIntent()
- : null;
- if (deleteIntent != null) {
- try {
- deleteIntent.send();
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Failed to send delete intent for bubble with key: "
- + (bubble.entry != null ? bubble.entry.key : " null entry"));
- }
- }
- }
- }
-
- /**
- * Updates a bubble in the stack.
- * @param entry the entry to update in the stack.
- */
- public void updateBubble(NotificationEntry entry) {
- Bubble b = mBubbleData.getBubble(entry.key);
- mBubbleData.updateBubble(entry.key, entry);
-
- if (!mIsExpanded) {
- // If alerting it gets promoted to top of the stack.
- if (mBubbleContainer.indexOfChild(b.iconView) != 0) {
- mBubbleContainer.moveViewTo(b.iconView, 0);
- }
- requestUpdate();
- animateInFlyoutForBubble(b /* bubble */);
- }
- if (mIsExpanded && entry.equals(mExpandedBubble.entry)) {
- entry.setShowInShadeWhenBubble(false);
- requestUpdate();
- }
- logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
- }
-
- /**
* @return the view the touch event is on
*/
@Nullable
@@ -701,7 +600,7 @@
return this;
}
- public View getFlyoutView() {
+ View getFlyoutView() {
return mFlyout;
}
@@ -714,13 +613,8 @@
*/
@Deprecated
@MainThread
- public void collapseStack() {
- if (mIsExpanded) {
- // TODO: Save opened bubble & move it to top of stack
- animateExpansion(false /* shouldExpand */);
- notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded);
- logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
- }
+ void collapseStack() {
+ mBubbleData.setExpanded(false);
}
/**
@@ -743,12 +637,8 @@
*/
@Deprecated
@MainThread
- public void expandStack() {
- if (!mIsExpanded) {
- String expandedBubbleKey = getBubbleAt(0).getKey();
- setExpandedBubble(expandedBubbleKey);
- logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
- }
+ void expandStack() {
+ mBubbleData.setExpanded(true);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index f882569..e7878c6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -119,6 +119,17 @@
}
}
updateSnapPosition();
+ updateSeparatedButtonSize();
+ }
+
+ private void updateSeparatedButtonSize() {
+ ViewGroup separated = getSeparatedView();
+ if (separated.getChildCount() == 1) {
+ View onlyChild = separated.getChildAt(0);
+ ViewGroup.LayoutParams childParams = onlyChild.getLayoutParams();
+ childParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ childParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 6f50baa..4590470 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -25,9 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Rect;
import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
@@ -37,13 +35,8 @@
import android.os.Trace;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
import android.text.TextUtils;
-import android.text.style.DynamicDrawableSpan;
-import android.text.style.ImageSpan;
import android.text.style.StyleSpan;
-import android.util.MathUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -205,9 +198,9 @@
addMediaLocked(builder);
} else {
builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
- addNextAlarmLocked(builder);
- addZenModeLocked(builder);
}
+ addNextAlarmLocked(builder);
+ addZenModeLocked(builder);
addPrimaryActionLocked(builder);
slice = builder.build();
}
@@ -221,32 +214,25 @@
protected void addMediaLocked(ListBuilder listBuilder) {
if (mMediaMetaData != null) {
- SpannableStringBuilder builder = new SpannableStringBuilder();
-
- Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
- if (notificationIcon != null) {
- Drawable drawable = notificationIcon.loadDrawable(getContext());
- Rect mediaBounds = new Rect(0 /* left */, 0 /* top */,
- drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
- int iconHeaderSize = getContext().getResources()
- .getDimensionPixelSize(R.dimen.header_icon_size);
- MathUtils.fitRect(mediaBounds, iconHeaderSize);
- drawable.setBounds(mediaBounds);
- builder.append("# ");
- builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER),
- 0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- }
-
CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
if (TextUtils.isEmpty(title)) {
title = getContext().getResources().getString(R.string.music_controls_no_title);
}
- builder.append(title);
- listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder));
+ listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title));
CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
if (!TextUtils.isEmpty(album)) {
- listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album));
+ RowBuilder albumBuilder = new RowBuilder(mMediaUri);
+ albumBuilder.setTitle(album);
+
+ Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+ IconCompat mediaIconCompat = mediaIcon == null ? null
+ : IconCompat.createFromIcon(getContext(), mediaIcon);
+ if (mediaIconCompat != null) {
+ albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE);
+ }
+
+ listBuilder.addRow(albumBuilder);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
index 0eeaa9b..7de8b74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -139,43 +139,49 @@
@Override
public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
- if (info.anySimReady) {
- boolean[] slotSeen = new boolean[SIM_SLOTS];
- if (info.listOfCarriers.length == info.subscriptionIds.length) {
- for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
- int slot = getSlotIndex(info.subscriptionIds[i]);
- if (slot >= SIM_SLOTS) {
- Log.w(TAG, "updateInfoCarrier - slot: " + slot);
- continue;
+ if (info.airplaneMode) {
+ setVisibility(View.GONE);
+ } else {
+ setVisibility(View.VISIBLE);
+ if (info.anySimReady) {
+ boolean[] slotSeen = new boolean[SIM_SLOTS];
+ if (info.listOfCarriers.length == info.subscriptionIds.length) {
+ for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
+ int slot = getSlotIndex(info.subscriptionIds[i]);
+ if (slot >= SIM_SLOTS) {
+ Log.w(TAG, "updateInfoCarrier - slot: " + slot);
+ continue;
+ }
+ if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.e(TAG,
+ "Invalid SIM slot index for subscription: "
+ + info.subscriptionIds[i]);
+ continue;
+ }
+ mInfos[slot].visible = true;
+ slotSeen[slot] = true;
+ mCarrierGroups[slot].setCarrierText(
+ info.listOfCarriers[i].toString().trim());
+ mCarrierGroups[slot].setVisibility(View.VISIBLE);
}
- if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- Log.e(TAG,
- "Invalid SIM slot index for subscription: "
- + info.subscriptionIds[i]);
- continue;
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ if (!slotSeen[i]) {
+ mInfos[i].visible = false;
+ mCarrierGroups[i].setVisibility(View.GONE);
+ }
}
- mInfos[slot].visible = true;
- slotSeen[slot] = true;
- mCarrierGroups[slot].setCarrierText(info.listOfCarriers[i].toString().trim());
- mCarrierGroups[slot].setVisibility(View.VISIBLE);
- }
- for (int i = 0; i < SIM_SLOTS; i++) {
- if (!slotSeen[i]) {
- mInfos[i].visible = false;
- mCarrierGroups[i].setVisibility(View.GONE);
- }
+ } else {
+ Log.e(TAG, "Carrier information arrays not of same length");
}
} else {
- Log.e(TAG, "Carrier information arrays not of same length");
- }
- } else {
- mInfos[0].visible = false;
- mCarrierGroups[0].setCarrierText(info.carrierText);
- mCarrierGroups[0].setVisibility(View.VISIBLE);
- for (int i = 1; i < SIM_SLOTS; i++) {
- mInfos[i].visible = false;
- mCarrierGroups[i].setCarrierText("");
- mCarrierGroups[i].setVisibility(View.GONE);
+ mInfos[0].visible = false;
+ mCarrierGroups[0].setCarrierText(info.carrierText);
+ mCarrierGroups[0].setVisibility(View.VISIBLE);
+ for (int i = 1; i < SIM_SLOTS; i++) {
+ mInfos[i].visible = false;
+ mCarrierGroups[i].setCarrierText("");
+ mCarrierGroups[i].setVisibility(View.GONE);
+ }
}
}
handleUpdateState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 2e35f06..97f2dd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -22,7 +22,6 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewParent;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleData;
@@ -120,7 +119,7 @@
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
if (ent.isRowDismissed() || ent.isRowRemoved()
- || (mBubbleData.getBubble(ent.key) != null && !ent.showInShadeWhenBubble())) {
+ || (mBubbleData.hasBubbleWithKey(ent.key) && !ent.showInShadeWhenBubble())) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
index 45fc756..1862ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java
@@ -22,6 +22,9 @@
import android.view.GestureDetector;
import android.view.MotionEvent;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
+
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
@@ -31,7 +34,7 @@
private static final int[] OPTED_OUT_STATE = new int[] { R.attr.optedOut };
private ConfirmedTapListener mConfirmedTapListener;
- private boolean mComponentEnabled = false;
+ private boolean mCaptionsEnabled = false;
private boolean mOptedOut = false;
private GestureDetector mGestureDetector;
@@ -39,16 +42,14 @@
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
- if (mConfirmedTapListener != null) {
- mConfirmedTapListener.onConfirmedTap();
- return true;
- }
- return false;
+ return tryToSendTapConfirmedEvent();
}
};
public CaptionsToggleImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
+ this.setContentDescription(
+ getContext().getString(R.string.volume_odi_captions_content_description));
}
@Override
@@ -66,16 +67,32 @@
return state;
}
- Runnable setComponentEnabled(boolean isComponentEnabled) {
- this.mComponentEnabled = isComponentEnabled;
+ Runnable setCaptionsEnabled(boolean areCaptionsEnabled) {
+ this.mCaptionsEnabled = areCaptionsEnabled;
- return this.setImageResourceAsync(this.mComponentEnabled
+ ViewCompat.replaceAccessibilityAction(
+ this,
+ AccessibilityActionCompat.ACTION_CLICK,
+ mCaptionsEnabled
+ ? getContext().getString(R.string.volume_odi_captions_hint_disable)
+ : getContext().getString(R.string.volume_odi_captions_hint_enable),
+ (view, commandArguments) -> tryToSendTapConfirmedEvent());
+
+ return this.setImageResourceAsync(mCaptionsEnabled
? R.drawable.ic_volume_odi_captions
: R.drawable.ic_volume_odi_captions_disabled);
}
- boolean getComponentEnabled() {
- return this.mComponentEnabled;
+ private boolean tryToSendTapConfirmedEvent() {
+ if (mConfirmedTapListener != null) {
+ mConfirmedTapListener.onConfirmedTap();
+ return true;
+ }
+ return false;
+ }
+
+ boolean getCaptionsEnabled() {
+ return this.mCaptionsEnabled;
}
/** Sets whether or not the current stream has opted out of captions */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 5095370..8d9c5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -596,9 +596,9 @@
}
private void updateCaptionsIcon() {
- boolean componentEnabled = mController.areCaptionsEnabled();
- if (mODICaptionsIcon.getComponentEnabled() != componentEnabled) {
- mHandler.post(mODICaptionsIcon.setComponentEnabled(componentEnabled));
+ boolean captionsEnabled = mController.areCaptionsEnabled();
+ if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) {
+ mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled));
}
boolean isOptedOut = mController.isCaptionStreamOptedOut();
@@ -878,7 +878,6 @@
}
view.setContentDescription(mContext.getString(currStateResId));
-
view.setAccessibilityDelegate(new AccessibilityDelegate() {
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 8e061cc..df534d7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -37,6 +37,7 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -81,6 +82,8 @@
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
private CarrierTextController.CarrierTextCallbackInfo mCarrierTextCallbackInfo;
private CarrierTextController mCarrierTextController;
@@ -94,6 +97,7 @@
mContext.addMockSystemService(WifiManager.class, mWifiManager);
mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
+ mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
mDependency.injectMockDependency(WakefulnessLifecycle.class);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
@@ -169,6 +173,7 @@
list.add(TEST_SUBSCRIPTION);
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -192,6 +197,7 @@
list.add(TEST_SUBSCRIPTION_ROAMING);
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+ when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
rename to packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java
index 26fa62b..4bb2395 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java
@@ -38,9 +38,9 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public final class StretchAnalogClockControllerTest extends SysuiTestCase {
+public final class AnalogClockControllerTest extends SysuiTestCase {
- private StretchAnalogClockController mClockController;
+ private AnalogClockController mClockController;
@Mock SysuiColorExtractor mMockColorExtractor;
@Before
@@ -49,7 +49,7 @@
Resources res = getContext().getResources();
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
- mClockController = new StretchAnalogClockController(res, layoutInflater,
+ mClockController = new AnalogClockController(res, layoutInflater,
mMockColorExtractor);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 20f539b..40a5357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -146,7 +146,7 @@
when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
- mBubbleData = new BubbleData();
+ mBubbleData = new BubbleData(mContext);
mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
mBubbleData, mConfigurationController);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
@@ -177,25 +177,27 @@
public void testRemoveBubble() {
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
-
+ verify(mNotificationEntryManager).updateNotifications();
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
assertFalse(mStatusBarWindowController.getBubblesShowing());
assertTrue(mRow.getEntry().isBubbleDismissed());
- verify(mNotificationEntryManager).updateNotifications();
+ verify(mNotificationEntryManager, times(2)).updateNotifications();
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
}
@Test
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
+ verify(mNotificationEntryManager, times(1)).updateNotifications();
mBubbleController.updateBubble(mRow2.getEntry());
+ verify(mNotificationEntryManager, times(2)).updateNotifications();
assertTrue(mBubbleController.hasBubbles());
mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mStatusBarWindowController.getBubblesShowing());
- verify(mNotificationEntryManager).updateNotifications();
+ verify(mNotificationEntryManager, times(3)).updateNotifications();
assertTrue(mRow.getEntry().isBubbleDismissed());
assertTrue(mRow2.getEntry().isBubbleDismissed());
}
@@ -215,7 +217,7 @@
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
- stackView.expandStack();
+ mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
assertTrue(mStatusBarWindowController.getBubbleExpanded());
@@ -224,7 +226,7 @@
assertFalse(mRow.getEntry().showInShadeWhenBubble());
// Collapse
- stackView.collapseStack();
+ mBubbleController.collapseStack();
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
assertFalse(mBubbleController.isStackExpanded());
assertFalse(mStatusBarWindowController.getBubbleExpanded());
@@ -245,23 +247,24 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- stackView.expandStack();
+ mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
- // Last added is the one that is expanded
- assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
- assertFalse(mRow2.getEntry().showInShadeWhenBubble());
-
- // Switch which bubble is expanded
- stackView.setExpandedBubble(mRow.getEntry());
+ // First added is the one that is expanded
assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
assertFalse(mRow.getEntry().showInShadeWhenBubble());
+ // Switch which bubble is expanded
+ mBubbleController.selectBubble(mRow2.getEntry().key);
+ stackView.setExpandedBubble(mRow2.getEntry());
+ assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
+ assertFalse(mRow2.getEntry().showInShadeWhenBubble());
+
// collapse for previous bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
// expand for selected bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
// Collapse
mBubbleController.collapseStack();
@@ -280,7 +283,7 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- stackView.expandStack();
+ mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
@@ -299,30 +302,30 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- stackView.expandStack();
+ mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
- // Last added is the one that is expanded
- assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
- assertFalse(mRow2.getEntry().showInShadeWhenBubble());
+ // First added is the one that is expanded
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
+ assertFalse(mRow.getEntry().showInShadeWhenBubble());
// Dismiss currently expanded
mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
BubbleController.DISMISS_USER_GESTURE);
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
// Make sure next bubble is selected
- assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
// Dismiss that one
mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
assertFalse(mBubbleController.hasBubbles());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
index 801308f..da31134 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
@@ -48,7 +48,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mStackView = new BubbleStackView(mContext, new BubbleData(), null);
+ mStackView = new BubbleStackView(mContext, new BubbleData(getContext()), null);
mBubble.entry = mNotifEntry;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 63d42a7..c534de7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.ContentResolver;
@@ -102,6 +103,7 @@
@Test
public void onBindSlice_readsMedia() {
MediaMetadata metadata = mock(MediaMetadata.class);
+ when(metadata.getText(any())).thenReturn("metadata");
mProvider.onDozingChanged(true);
mProvider.onMetadataChanged(metadata);
mProvider.onBindSlice(mProvider.getUri());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 2172a12..3662c38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -98,7 +98,7 @@
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
- () -> mShadeController, new BubbleData());
+ () -> mShadeController, new BubbleData(mContext));
Dependency.get(InitController.class).executePostInitTasks();
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index fdc3567..a94d1dc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -806,6 +806,17 @@
mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName);
return options;
}
+
+ @Override
+ public boolean isAugmentedAutofillServiceForUser(int callingUid, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.isAugmentedAutofillServiceForUserLocked(callingUid);
+ }
+ }
+ return false;
+ }
}
/**
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4bd6fbd..f1963b3 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1165,6 +1165,11 @@
return true;
}
+ boolean isAugmentedAutofillServiceForUserLocked(int callingUid) {
+ return mRemoteAugmentedAutofillServiceInfo != null
+ && mRemoteAugmentedAutofillServiceInfo.applicationInfo.uid == callingUid;
+ }
+
/**
* Sets which packages and activities can trigger augmented autofill.
*
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index adf5829..609904b 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -17,6 +17,7 @@
package com.android.server.autofill;
import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -99,7 +100,7 @@
}
try {
if (state) {
- mService.onConnected();
+ mService.onConnected(sDebug, sVerbose);
} else {
mService.onDisconnected();
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index fdc01e0..9e73684d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -265,7 +265,7 @@
// main reason being the cases where user tap HOME.
// Right now it's completely destroying the UI, but we need to decide whether / how to
// properly recover it later (for example, if the user switches back to the activity,
- // should it be restored? Right not it kind of is, because Autofill's Session trigger a
+ // should it be restored? Right now it kind of is, because Autofill's Session trigger a
// new FillRequest, which in turn triggers the Augmented Autofill request again)
@GuardedBy("mLock")
@Nullable
@@ -2755,9 +2755,6 @@
viewState.setState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
final AutofillValue currentValue = viewState.getCurrentValue();
- // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
- // further AFM -> AFMS calls.
-
if (mAugmentedRequestsLogs == null) {
mAugmentedRequestsLogs = new ArrayList<>();
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 447bd8c..03f2149 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -566,10 +566,6 @@
// require frequent starting and stopping.
mConstants.start();
- // Set up the various sorts of package tracking we do
- mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
- initPackageTracking();
-
// Build our mapping of uid to backup client services. This implicitly
// schedules a backup pass on the Package Manager metadata the first
// time anything needs to be backed up.
@@ -589,6 +585,10 @@
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*-" + userId);
+
+ // Set up the various sorts of package tracking we do
+ mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+ initPackageTracking();
}
void initializeBackupEnableState() {
@@ -744,6 +744,11 @@
return mDataDir;
}
+ @VisibleForTesting
+ BroadcastReceiver getPackageTrackingReceiver() {
+ return mBroadcastReceiver;
+ }
+
@Nullable
public DataChangedJournal getJournal() {
return mJournal;
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 593bb2c..35dfccf 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -23,22 +23,20 @@
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
-import android.app.AppGlobals;
import android.app.backup.BackupTransport;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import com.android.server.backup.transport.TransportClient;
import com.google.android.collect.Sets;
@@ -67,12 +65,13 @@
* </ol>
*/
public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
- return appIsEligibleForBackup(app, AppGlobals.getPackageManager(), userId);
+ return appIsEligibleForBackup(
+ app, LocalServices.getService(PackageManagerInternal.class), userId);
}
@VisibleForTesting
- static boolean appIsEligibleForBackup(ApplicationInfo app,
- IPackageManager packageManager, int userId) {
+ static boolean appIsEligibleForBackup(
+ ApplicationInfo app, PackageManagerInternal packageManager, int userId) {
// 1. their manifest states android:allowBackup="false"
if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
@@ -108,9 +107,9 @@
/**
* Returns whether an app is eligible for backup at runtime. That is, the app has to:
* <ol>
- * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, PackageManager)}
+ * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, int)}
* <li>Return false for {@link #appIsStopped(ApplicationInfo)}
- * <li>Return false for {@link #appIsDisabled(ApplicationInfo, PackageManager)}
+ * <li>Return false for {@link #appIsDisabled(ApplicationInfo, int)}
* <li>Be eligible for the transport via
* {@link BackupTransport#isAppEligibleForBackup(PackageInfo, boolean)}
* </ol>
@@ -149,19 +148,13 @@
/** Avoid backups of 'disabled' apps. */
static boolean appIsDisabled(ApplicationInfo app, int userId) {
- return appIsDisabled(app, AppGlobals.getPackageManager(), userId);
+ return appIsDisabled(app, LocalServices.getService(PackageManagerInternal.class), userId);
}
@VisibleForTesting
- static boolean appIsDisabled(ApplicationInfo app,
- IPackageManager packageManager, int userId) {
- int enabledSetting;
- try {
- enabledSetting = packageManager.getApplicationEnabledSetting(app.packageName, userId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get application enabled setting: " + e);
- return false;
- }
+ static boolean appIsDisabled(
+ ApplicationInfo app, PackageManagerInternal packageManager, int userId) {
+ int enabledSetting = packageManager.getApplicationEnabledState(app.packageName, userId);
switch (enabledSetting) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 382fdec..053e686 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -293,7 +293,6 @@
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";
@@ -349,10 +348,6 @@
@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<>();
@@ -1074,6 +1069,15 @@
mVold.onUserStarted(userId, packages, appIds, sandboxIds);
}
+ private boolean supportsBlockCheckpoint() throws RemoteException {
+ // Only the system process is permitted to start checkpoints
+ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("no permission to check block based checkpoint support");
+ }
+
+ return mVold.supportsBlockCheckpoint();
+ }
+
@Override
public void onAwakeStateChanged(boolean isAwake) {
// Ignored
@@ -1681,63 +1685,6 @@
mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
} catch (RemoteException e) {
}
-
- synchronized (mLock) {
- final boolean thisIsolatedStorage = StorageManager.hasIsolatedStorage();
- if (mLastIsolatedStorage != thisIsolatedStorage) {
- 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.
- *
- * @see com.android.server.pm.permission.PermissionManagerService
- * #applyLegacyStoragePermissionModel
- */
- private void applyLegacyStorage() {
- final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
- for (int userId : um.getUserIds()) {
- final UserHandle user = UserHandle.of(userId);
- final PackageManager pm;
- try {
- pm = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
- user).getPackageManager();
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- final List<PackageInfo> pkgs = pm.getPackagesHoldingPermissions(
- ALL_STORAGE_PERMISSIONS,
- MATCH_UNINSTALLED_PACKAGES | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
- | GET_PERMISSIONS);
- 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.setUidMode(AppOpsManager.OP_LEGACY_STORAGE, uid,
- AppOpsManager.MODE_ALLOWED);
- }
- }
- }
}
private static long getLastAccessTime(AppOpsManager manager,
@@ -1783,7 +1730,6 @@
private void readSettingsLocked() {
mRecords.clear();
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
- mLastIsolatedStorage = false;
FileInputStream fis = null;
try {
@@ -1805,9 +1751,6 @@
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);
mRecords.put(rec.fsUuid, rec);
@@ -1837,7 +1780,6 @@
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);
@@ -2116,37 +2058,45 @@
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
try {
- mVold.fstrim(flags, new IVoldTaskListener.Stub() {
- @Override
- public void onStatus(int status, PersistableBundle extras) {
- dispatchOnStatus(listener, status, extras);
+ // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
+ // (first boot after OTA), We skip idle maintenance and make sure the last
+ // fstrim time is still updated. If file based checkpoints are used, we run
+ // idle maintenance (GC + fstrim) regardless of checkpoint status.
+ if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
+ mVold.fstrim(flags, new IVoldTaskListener.Stub() {
+ @Override
+ public void onStatus(int status, PersistableBundle extras) {
+ dispatchOnStatus(listener, status, extras);
- // Ignore trim failures
- if (status != 0) return;
+ // Ignore trim failures
+ if (status != 0) return;
- final String path = extras.getString("path");
- final long bytes = extras.getLong("bytes");
- final long time = extras.getLong("time");
+ final String path = extras.getString("path");
+ final long bytes = extras.getLong("bytes");
+ final long time = extras.getLong("time");
- final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
- dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time);
+ final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
+ dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time);
- synchronized (mLock) {
- final VolumeRecord rec = findRecordForPath(path);
- if (rec != null) {
- rec.lastTrimMillis = System.currentTimeMillis();
- writeSettingsLocked();
+ synchronized (mLock) {
+ final VolumeRecord rec = findRecordForPath(path);
+ if (rec != null) {
+ rec.lastTrimMillis = System.currentTimeMillis();
+ writeSettingsLocked();
+ }
}
}
- }
- @Override
- public void onFinished(int status, PersistableBundle extras) {
- dispatchOnFinished(listener, status, extras);
+ @Override
+ public void onFinished(int status, PersistableBundle extras) {
+ dispatchOnFinished(listener, status, extras);
- // TODO: benchmark when desired
- }
- });
+ // TODO: benchmark when desired
+ }
+ });
+ } else {
+ Slog.i(TAG, "Skipping fstrim - block based checkpoint in progress");
+ }
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -2156,18 +2106,26 @@
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
try {
- mVold.runIdleMaint(new IVoldTaskListener.Stub() {
- @Override
- public void onStatus(int status, PersistableBundle extras) {
- // Not currently used
- }
- @Override
- public void onFinished(int status, PersistableBundle extras) {
- if (callback != null) {
- BackgroundThread.getHandler().post(callback);
+ // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
+ // (first boot after OTA), We skip idle maintenance and make sure the last
+ // fstrim time is still updated. If file based checkpoints are used, we run
+ // idle maintenance (GC + fstrim) regardless of checkpoint status.
+ if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
+ mVold.runIdleMaint(new IVoldTaskListener.Stub() {
+ @Override
+ public void onStatus(int status, PersistableBundle extras) {
+ // Not currently used
}
- }
- });
+ @Override
+ public void onFinished(int status, PersistableBundle extras) {
+ if (callback != null) {
+ BackgroundThread.getHandler().post(callback);
+ }
+ }
+ });
+ } else {
+ Slog.i(TAG, "Skipping idle maintenance - block based checkpoint in progress");
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3766,7 +3724,7 @@
return Zygote.MOUNT_EXTERNAL_NONE;
}
if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) {
- return Zygote.MOUNT_EXTERNAL_NONE;
+ return Zygote.MOUNT_EXTERNAL_DEFAULT;
}
// Determine if caller is holding runtime permission
@@ -3800,11 +3758,7 @@
// they hold the runtime permission
final boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,
uid, packageName) == MODE_ALLOWED;
- // STOPSHIP: only use app-op once permission model has fully landed
- final boolean requestedLegacy = !mIPackageManager
- .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid))
- .isExternalStorageSandboxAllowed();
- if ((hasLegacy || requestedLegacy) && hasStorage) {
+ if (hasLegacy && hasStorage) {
return Zygote.MOUNT_EXTERNAL_LEGACY;
} else {
return Zygote.MOUNT_EXTERNAL_WRITE;
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 6bb3200..1ce65561 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -120,7 +120,7 @@
private static void dumpStackTraces() {
final ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
- ActivityManagerService.dumpStackTraces(
- pids, null, null, null);
+ ActivityManagerService.dumpStackTraces(pids, null, null,
+ Watchdog.getInterestingNativePids());
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index afcf954..cc3b43a 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -101,17 +101,18 @@
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
- "android.hardware.audio@2.0::IDevicesFactory",
- "android.hardware.audio@4.0::IDevicesFactory",
- "android.hardware.bluetooth@1.0::IBluetoothHci",
- "android.hardware.camera.provider@2.4::ICameraProvider",
- "android.hardware.graphics.allocator@2.0::IAllocator",
- "android.hardware.graphics.composer@2.1::IComposer",
- "android.hardware.health@2.0::IHealth",
- "android.hardware.media.omx@1.0::IOmx",
- "android.hardware.media.omx@1.0::IOmxStore",
- "android.hardware.sensors@1.0::ISensors",
- "android.hardware.vr@1.0::IVr"
+ "android.hardware.audio@2.0::IDevicesFactory",
+ "android.hardware.audio@4.0::IDevicesFactory",
+ "android.hardware.bluetooth@1.0::IBluetoothHci",
+ "android.hardware.camera.provider@2.4::ICameraProvider",
+ "android.hardware.graphics.allocator@2.0::IAllocator",
+ "android.hardware.graphics.composer@2.1::IComposer",
+ "android.hardware.health@2.0::IHealth",
+ "android.hardware.media.omx@1.0::IOmx",
+ "android.hardware.media.omx@1.0::IOmxStore",
+ "android.hardware.sensors@1.0::ISensors",
+ "android.hardware.vr@1.0::IVr",
+ "android.hardware.biometrics.face@1.0::IBiometricsFace"
);
static Watchdog sWatchdog;
@@ -396,7 +397,7 @@
return builder.toString();
}
- private ArrayList<Integer> getInterestingHalPids() {
+ private static ArrayList<Integer> getInterestingHalPids() {
try {
IServiceManager serviceManager = IServiceManager.getService();
ArrayList<IServiceManager.InstanceDebugInfo> dump =
@@ -419,7 +420,7 @@
}
}
- private ArrayList<Integer> getInterestingNativePids() {
+ static ArrayList<Integer> getInterestingNativePids() {
ArrayList<Integer> pids = getInterestingHalPids();
int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4ec90ba..0271354 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1806,7 +1806,7 @@
|| (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
&& (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
b.client);
- mAm.updateOomAdjLocked();
+ mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
@@ -1957,11 +1957,12 @@
r.binding.service.app.hasClientActivities()
|| r.binding.service.app.treatLikeActivity, null);
}
- mAm.updateOomAdjLocked(r.binding.service.app, false);
+ mAm.updateOomAdjLocked(r.binding.service.app, false,
+ OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
}
- mAm.updateOomAdjLocked();
+ mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2676,7 +2677,7 @@
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
- mAm.updateOomAdjLocked();
+ mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
boolean created = false;
try {
@@ -2797,7 +2798,7 @@
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
- mAm.updateOomAdjLocked(r.app, true);
+ mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
}
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
@@ -2923,7 +2924,8 @@
if (ibr.hasBound) {
try {
bumpServiceExecutingLocked(r, false, "bring down unbind");
- mAm.updateOomAdjLocked(r.app, true);
+ mAm.updateOomAdjLocked(r.app, true,
+ OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
ibr.hasBound = false;
ibr.requested = false;
r.app.thread.scheduleUnbindService(r,
@@ -3038,7 +3040,8 @@
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
r.destroying = true;
- mAm.updateOomAdjLocked(r.app, true);
+ mAm.updateOomAdjLocked(r.app, true,
+ OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
Slog.w(TAG, "Exception when destroying service "
@@ -3143,7 +3146,8 @@
// it to go down there and we want it to start out near the top.
mAm.updateLruProcessLocked(s.app, false, null);
}
- mAm.updateOomAdjLocked(s.app, true);
+ mAm.updateOomAdjLocked(s.app, true,
+ OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
@@ -3296,7 +3300,7 @@
mDestroyingServices.remove(r);
r.bindings.clear();
}
- mAm.updateOomAdjLocked(r.app, true);
+ mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
r.executeFg = false;
if (r.tracker != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3b6b404..cdcd9e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1972,7 +1972,7 @@
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
mPidsSelfLocked.put(app.pid, app);
mProcessList.updateLruProcessLocked(app, false, null);
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
@@ -2456,7 +2456,7 @@
// bind background threads to little cores
// this is expected to fail inside of framework tests because apps can't touch cpusets directly
// make sure we've already adjusted system_server's internal view of itself first
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
try {
Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
Process.THREAD_GROUP_SYSTEM);
@@ -3634,7 +3634,7 @@
handleAppDiedLocked(app, false, true);
if (doOomAdj) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
}
if (doLowMem) {
doLowMemReportIfNeededLocked(app);
@@ -5002,7 +5002,7 @@
}
if (!didSomething) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
@@ -5485,7 +5485,7 @@
synchronized (this) {
mConstants.setOverrideMaxCachedProcesses(max);
}
- trimApplications();
+ trimApplications(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
}
@Override
@@ -5511,7 +5511,7 @@
pr.forcingToImportant = null;
updateProcessForegroundLocked(pr, false, 0, false);
}
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
@@ -5557,7 +5557,7 @@
}
if (changed) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
}
@@ -6714,7 +6714,8 @@
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
final int verifiedAdj = cpr.proc.verifiedAdj;
- boolean success = updateOomAdjLocked(cpr.proc, true);
+ boolean success = updateOomAdjLocked(cpr.proc, true,
+ OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
// if the process has been successfully adjusted. So to reduce races with
// it, we will check whether the process still exists. Note that this doesn't
@@ -7147,7 +7148,7 @@
throw new NullPointerException("connection is null");
}
if (decProviderCountLocked(conn, null, null, stable)) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
}
}
} finally {
@@ -7188,7 +7189,7 @@
ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
if (localCpr.hasExternalProcessHandles()) {
if (localCpr.removeExternalProcessHandleLocked(token)) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
} else {
Slog.e(TAG, "Attmpt to remove content provider " + localCpr
+ " with no external reference for token: "
@@ -7255,7 +7256,7 @@
dst.setProcess(r);
dst.notifyAll();
}
- updateOomAdjLocked(r, true);
+ updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
@@ -7645,7 +7646,7 @@
new HostingRecord("added application",
customProcess != null ? customProcess : info.processName));
mProcessList.updateLruProcessLocked(app, false, null);
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
}
// This package really, really can not be stopped.
@@ -7740,7 +7741,7 @@
mActivityTaskManager.onScreenAwakeChanged(isAwake);
mOomAdjProfiler.onWakefulnessChanged(wakefulness);
}
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
@@ -8320,7 +8321,7 @@
}
}
if (changed) {
- updateOomAdjLocked(pr, true);
+ updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
} finally {
@@ -8350,7 +8351,7 @@
Slog.i(TAG, "Setting runningRemoteAnimation=" + pr.runningRemoteAnimation
+ " for pid=" + pid);
}
- updateOomAdjLocked(pr, true);
+ updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
@@ -13984,7 +13985,7 @@
mBackupTargets.put(targetUserId, r);
// Try not to kill the process during backup
- updateOomAdjLocked(proc, true);
+ updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE);
// If the process is already attached, schedule the creation of the backup agent now.
// If it is not yet live, this will be done when it attaches to the framework.
@@ -14099,7 +14100,7 @@
// Not backing this app up any more; reset its OOM adjustment
final ProcessRecord proc = backupTarget.app;
- updateOomAdjLocked(proc, true);
+ updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE);
proc.inFullBackup = false;
oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
@@ -14388,7 +14389,7 @@
// If we actually concluded any broadcasts, we might now be able
// to trim the recipients' apps from our working set
if (doTrim) {
- trimApplications();
+ trimApplications(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
return;
}
@@ -15463,7 +15464,7 @@
r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
}
// updateOomAdjLocked() will be done here
- trimApplicationsLocked();
+ trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
}
} finally {
@@ -16448,7 +16449,7 @@
item.foregroundServiceTypes = fgServiceTypes;
if (oomAdj) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
}
@@ -16496,11 +16497,13 @@
* @param app The process to update
* @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps
* if necessary, or skip.
+ * @param oomAdjReason
* @return whether updateOomAdjLocked(app) was successful.
*/
@GuardedBy("this")
- final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
- return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll);
+ final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll,
+ String oomAdjReason) {
+ return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll, oomAdjReason);
}
static final class ProcStatsRunnable implements Runnable {
@@ -16693,8 +16696,8 @@
}
@GuardedBy("this")
- final void updateOomAdjLocked() {
- mOomAdjuster.updateOomAdjLocked();
+ final void updateOomAdjLocked(String oomAdjReason) {
+ mOomAdjuster.updateOomAdjLocked(oomAdjReason);
}
@Override
@@ -16979,14 +16982,14 @@
mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist);
}
- final void trimApplications() {
+ final void trimApplications(String oomAdjReason) {
synchronized (this) {
- trimApplicationsLocked();
+ trimApplicationsLocked(oomAdjReason);
}
}
@GuardedBy("this")
- final void trimApplicationsLocked() {
+ final void trimApplicationsLocked(String oomAdjReason) {
// First remove any unused application processes whose package
// has been removed.
for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
@@ -17018,7 +17021,7 @@
// Now update the oom adj for all processes. Don't skip this, since other callers
// might be depending on it.
- updateOomAdjLocked();
+ updateOomAdjLocked(oomAdjReason);
}
/** This method sends the specified signal to each of the persistent apps */
@@ -17627,7 +17630,7 @@
}
pr.setHasOverlayUi(hasOverlayUi);
//Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
- updateOomAdjLocked(pr, true);
+ updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
@@ -17784,7 +17787,7 @@
@Override
public void trimApplications() {
- ActivityManagerService.this.trimApplications();
+ ActivityManagerService.this.trimApplications(OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
}
public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
@@ -17836,7 +17839,7 @@
@Override
public void updateOomAdj() {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.updateOomAdjLocked();
+ ActivityManagerService.this.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 3c57c3b..ee9b561 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -309,7 +309,7 @@
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.mProcessList.updateLruProcessLocked(app, false, null);
if (!skipOomAdj) {
- mService.updateOomAdjLocked();
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
// Tell the application to launch this receiver.
@@ -791,7 +791,8 @@
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceivers.add(r);
- mService.updateOomAdjLocked(r.curApp, true);
+ mService.updateOomAdjLocked(r.curApp, true,
+ OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
}
}
try {
@@ -1028,7 +1029,7 @@
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
- mService.updateOomAdjLocked();
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
}
// when we have no more ordered broadcast on this queue, stop logging
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 44d435f..8ae7c7d 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -90,6 +90,21 @@
public final class OomAdjuster {
private static final String TAG = "OomAdjuster";
+ static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
+ static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
+ static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange";
+ static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver";
+ static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver";
+ static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService";
+ static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService";
+ static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService";
+ static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider";
+ static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider";
+ static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility";
+ static final String OOM_ADJ_REASON_WHITELIST = OOM_ADJ_REASON_METHOD + "_whitelistChange";
+ static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin";
+ static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
+
/**
* For some direct access we need to power manager.
*/
@@ -156,10 +171,12 @@
* @param app The process to update
* @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps
* if necessary, or skip.
+ * @param oomAdjReason
* @return whether updateOomAdjLocked(app) was successful.
*/
@GuardedBy("mService")
- final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
+ boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll,
+ String oomAdjReason) {
final ProcessRecord TOP_APP = mService.getTopAppLocked();
final boolean wasCached = app.cached;
@@ -177,7 +194,7 @@
&& (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
- updateOomAdjLocked();
+ updateOomAdjLocked(oomAdjReason);
}
return success;
}
@@ -195,8 +212,8 @@
}
@GuardedBy("mService")
- final void updateOomAdjLocked() {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "updateOomAdj");
+ void updateOomAdjLocked(String oomAdjReason) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
mService.mOomAdjProfiler.oomAdjStarted();
final ProcessRecord TOP_APP = mService.getTopAppLocked();
final long now = SystemClock.uptimeMillis();
@@ -2009,7 +2026,7 @@
}
}
if (changed) {
- updateOomAdjLocked();
+ updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST);
}
}
@@ -2019,7 +2036,7 @@
final UidRecord uidRec = mActiveUids.get(uid);
if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
uidRec.curWhitelist = onWhitelist;
- updateOomAdjLocked();
+ updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST);
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index a08c829..588e05d 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -29,6 +29,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
@@ -380,8 +381,9 @@
userId = controller.mUserController.getCurrentOrTargetUserId();
}
// temporarily allow receivers and services to open activities from background if the
- // PendingIntent.send() caller was foreground at the time of sendInner() call
- final boolean allowTrampoline = uid != callingUid
+ // PendingIntent.send() caller was foreground at the time of sendInner() call, unless
+ // caller is SYSTEM_UID
+ final boolean allowTrampoline = uid != callingUid && callingUid != Process.SYSTEM_UID
&& controller.mAtmInternal.isUidForeground(callingUid);
// note: we on purpose don't pass in the information about the PendingIntent's creator,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0a926f9..316368a 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2227,9 +2227,10 @@
for (AppZygote appZygote : zygotesToKill) {
killAppZygoteIfNeededLocked(appZygote);
}
- mService.updateOomAdjLocked();
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
return N > 0;
}
+
@GuardedBy("mService")
boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 933f41c..49930c1 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1301,7 +1301,7 @@
}
mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */);
if (updateOomAdj) {
- mService.updateOomAdjLocked();
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
}
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 873cadb..cd8167a 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3097,6 +3097,7 @@
int nonpackageUid;
final static Binder sBinder = new Binder();
IBinder mToken;
+ boolean targetsUid;
Shell(IAppOpsService iface, AppOpsService internal) {
mInterface = iface;
@@ -3190,6 +3191,8 @@
for (String argument; (argument = getNextArg()) != null;) {
if ("--user".equals(argument)) {
userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else if ("--uid".equals(argument)) {
+ targetsUid = true;
} else {
if (packageName == null) {
packageName = argument;
@@ -3288,7 +3291,7 @@
pw.println(" Starts a given operation for a particular application.");
pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> ");
pw.println(" Stops a given operation for a particular application.");
- pw.println(" set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>");
+ pw.println(" set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>");
pw.println(" Set the mode for a particular application and operation.");
pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]");
pw.println(" Return the mode for a particular application and optional operation.");
@@ -3306,6 +3309,7 @@
pw.println(" <MODE> one of allow, ignore, deny, or default");
pw.println(" <USER_ID> the user id under which the package is installed. If --user is not");
pw.println(" specified, the current user is assumed.");
+ pw.println(" --uid PACKAGE refer to the UID of the package");
}
static int onShellCommand(Shell shell, String cmd) {
@@ -3332,9 +3336,17 @@
return -1;
}
- if (shell.packageName != null) {
+ if (!shell.targetsUid && shell.packageName != null) {
shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName,
mode);
+ } else if (shell.targetsUid && shell.packageName != null) {
+ try {
+ final int uid = shell.mInternal.mContext.getPackageManager()
+ .getPackageUid(shell.packageName, shell.userId);
+ shell.mInterface.setUidMode(shell.op, uid, mode);
+ } catch (PackageManager.NameNotFoundException e) {
+ return -1;
+ }
} else {
shell.mInterface.setUidMode(shell.op, shell.nonpackageUid, mode);
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 411dd79..d7d4851 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -58,6 +58,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -100,16 +101,25 @@
private final Object mLock;
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
- private final AttentionHandler mAttentionHandler;
+ private AttentionHandler mAttentionHandler;
- private ComponentName mComponentName;
+ @VisibleForTesting
+ ComponentName mComponentName;
public AttentionManagerService(Context context) {
+ this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+ new Object(), null);
+ mAttentionHandler = new AttentionHandler();
+ }
+
+ @VisibleForTesting
+ AttentionManagerService(Context context, PowerManager powerManager, Object lock,
+ AttentionHandler handler) {
super(context);
mContext = Preconditions.checkNotNull(context);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mLock = new Object();
- mAttentionHandler = new AttentionHandler();
+ mPowerManager = powerManager;
+ mLock = lock;
+ mAttentionHandler = handler;
}
@Override
@@ -147,7 +157,8 @@
return isServiceEnabled() && isServiceAvailable();
}
- private boolean isServiceEnabled() {
+ @VisibleForTesting
+ protected boolean isServiceEnabled() {
return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED,
DEFAULT_SERVICE_ENABLED);
}
@@ -161,7 +172,8 @@
*
* @return {@code true} if the framework was able to dispatch the request
*/
- private boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
+ @VisibleForTesting
+ boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
Preconditions.checkNotNull(callbackInternal);
if (!isAttentionServiceSupported()) {
@@ -257,24 +269,24 @@
}
/** Cancels the specified attention check. */
- private void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
+ @VisibleForTesting
+ void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
synchronized (mLock) {
final UserState userState = peekCurrentUserStateLocked();
if (userState == null) {
return;
}
-
if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) {
Slog.e(LOG_TAG, "Cannot cancel a non-current request");
return;
}
-
cancel(userState);
}
}
@GuardedBy("mLock")
- private void freeIfInactiveLocked() {
+ @VisibleForTesting
+ protected void freeIfInactiveLocked() {
// If we are called here, it means someone used the API again - reset the timer then.
mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION);
@@ -291,7 +303,8 @@
@GuardedBy("mLock")
- private UserState getOrCreateCurrentUserStateLocked() {
+ @VisibleForTesting
+ protected UserState getOrCreateCurrentUserStateLocked() {
return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
}
@@ -307,7 +320,8 @@
@GuardedBy("mLock")
@Nullable
- private UserState peekCurrentUserStateLocked() {
+ @VisibleForTesting
+ protected UserState peekCurrentUserStateLocked() {
return peekUserStateLocked(ActivityManager.getCurrentUser());
}
@@ -418,7 +432,8 @@
}
}
- private static final class AttentionCheck {
+ @VisibleForTesting
+ static final class AttentionCheck {
private final AttentionCallbackInternal mCallbackInternal;
private final IAttentionCallback mIAttentionCallback;
private boolean mIsDispatched;
@@ -435,7 +450,8 @@
}
}
- private static final class UserState {
+ @VisibleForTesting
+ protected static class UserState {
final ComponentName mComponentName;
final AttentionServiceConnection mConnection = new AttentionServiceConnection();
@@ -453,7 +469,7 @@
final Context mContext;
final Object mLock;
- private UserState(int userId, Context context, Object lock, ComponentName componentName) {
+ UserState(int userId, Context context, Object lock, ComponentName componentName) {
mUserId = userId;
mContext = Preconditions.checkNotNull(context);
mLock = Preconditions.checkNotNull(lock);
@@ -509,7 +525,7 @@
}
}
- private final class AttentionServiceConnection implements ServiceConnection {
+ private class AttentionServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
init(IAttentionService.Stub.asInterface(service));
@@ -544,7 +560,8 @@
}
}
- private class AttentionHandler extends Handler {
+ @VisibleForTesting
+ protected class AttentionHandler extends Handler {
private static final int CHECK_CONNECTION_EXPIRATION = 1;
private static final int ATTENTION_CHECK_TIMEOUT = 2;
@@ -576,7 +593,8 @@
}
}
- private void cancel(UserState userState) {
+ @VisibleForTesting
+ void cancel(UserState userState) {
if (userState == null || userState.mCurrentAttentionCheck == null) {
return;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 44c1715..c5733322 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -114,8 +114,10 @@
// All post* methods are asynchronous
/*package*/ void onSystemReady() {
- synchronized (mDeviceStateLock) {
- mBtHelper.onSystemReady();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onSystemReady();
+ }
}
}
@@ -151,8 +153,10 @@
* @param intent
*/
/*package*/ void receiveBtEvent(@NonNull Intent intent) {
- synchronized (mDeviceStateLock) {
- mBtHelper.receiveBtEvent(intent);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.receiveBtEvent(intent);
+ }
}
}
@@ -350,13 +354,19 @@
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
}
+ @GuardedBy("mSetModeLock")
/*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+ }
}
+ @GuardedBy("mSetModeLock")
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ }
}
//---------------------------------------------------------------------
@@ -479,6 +489,10 @@
hearingAidProfile);
}
+ /*package*/ void postScoClientDied(Object obj) {
+ sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+ }
+
//---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method
@@ -708,8 +722,10 @@
}
break;
case MSG_BT_HEADSET_CNCT_FAILED:
- synchronized (mDeviceStateLock) {
- mBtHelper.resetBluetoothSco();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.resetBluetoothSco();
+ }
}
break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT:
@@ -742,8 +758,17 @@
}
break;
case MSG_I_DISCONNECT_BT_SCO:
- synchronized (mDeviceStateLock) {
- mBtHelper.disconnectBluetoothSco(msg.arg1);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectBluetoothSco(msg.arg1);
+ }
+ }
+ break;
+ case MSG_L_SCOCLIENT_DIED:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.scoClientDied(msg.arg1);
+ }
}
break;
case MSG_TOGGLE_HDMI:
@@ -774,8 +799,10 @@
}
break;
case MSG_DISCONNECT_BT_HEADSET:
- synchronized (mDeviceStateLock) {
- mBtHelper.disconnectHeadset();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectHeadset();
+ }
}
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
@@ -794,8 +821,10 @@
}
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
- synchronized (mDeviceStateLock) {
- mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ }
}
break;
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: {
@@ -892,6 +921,8 @@
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28;
// process external command to (dis)connect or change active A2DP device
private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT = 29;
+ // a ScoClient died in BtHelper
+ private static final int MSG_L_SCOCLIENT_DIED = 30;
private static boolean isMessageHandledUnderWakelock(int msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index aee08bb..d30a9d2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3484,7 +3484,9 @@
!mSystemReady) {
return;
}
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
+ synchronized (mDeviceBroker.mSetModeLock) {
+ mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
+ }
}
/** @see AudioManager#stopBluetoothSco() */
@@ -3496,7 +3498,9 @@
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
+ synchronized (mDeviceBroker.mSetModeLock) {
+ mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
+ }
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 2d9156b..332ff36 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -36,6 +36,8 @@
import android.provider.Settings;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
@@ -163,6 +165,8 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -231,6 +235,8 @@
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
@@ -317,6 +323,8 @@
*
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
@@ -325,6 +333,8 @@
clearAllScoClients(exceptPid, true);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, true);
@@ -344,6 +354,8 @@
Binder.restoreCallingIdentity(ident);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, false);
@@ -401,6 +413,8 @@
mDeviceBroker.postDisconnectHearingAid();
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
@@ -409,6 +423,8 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectHeadset() {
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
@@ -454,6 +470,8 @@
/*eventSource*/ "mBluetoothProfileServiceListener");
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -540,6 +558,9 @@
return result;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
@@ -621,6 +642,20 @@
};
//----------------------------------------------------------------------
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ /*package*/ synchronized void scoClientDied(Object obj) {
+ final ScoClient client = (ScoClient) obj;
+ Log.w(TAG, "SCO client died");
+ int index = mScoClients.indexOf(client);
+ if (index < 0) {
+ Log.w(TAG, "unregistered SCO client died");
+ } else {
+ client.clearCount(true);
+ mScoClients.remove(client);
+ }
+ }
+
private class ScoClient implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
private int mCreatorPid;
@@ -634,21 +669,14 @@
@Override
public void binderDied() {
- // this is the only place the implementation of ScoClient needs to be synchronized
- // on the instance, as all other methods are directly or indirectly called from
- // package-private methods, which are synchronized
- synchronized (BtHelper.this) {
- Log.w(TAG, "SCO client died");
- int index = mScoClients.indexOf(this);
- if (index < 0) {
- Log.w(TAG, "unregistered SCO client died");
- } else {
- clearCount(true);
- mScoClients.remove(this);
- }
- }
+ // process this from DeviceBroker's message queue to take the right locks since
+ // this event can impact SCO mode and requires querying audio mode stack
+ mDeviceBroker.postScoClientDied(this);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
void incCount(int scoAudioMode) {
requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) {
@@ -663,6 +691,9 @@
mStartcount++;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
void decCount() {
if (mStartcount == 0) {
Log.w(TAG, "ScoClient.decCount() already 0");
@@ -679,6 +710,9 @@
}
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
void clearCount(boolean stopSco) {
if (mStartcount != 0) {
try {
@@ -714,6 +748,9 @@
return count;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
private void requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
int clientCount = totalCount();
@@ -728,74 +765,71 @@
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
// currently controlled by the same client process.
- // TODO do not sync that way, see b/123769055
- synchronized (mDeviceBroker.mSetModeLock) {
- int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty()
- ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
- if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
- Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
- + modeOwnerPid + " != creatorPid " + mCreatorPid);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return;
- }
- switch (mScoAudioState) {
- case SCO_STATE_INACTIVE:
- mScoAudioMode = scoAudioMode;
- if (scoAudioMode == SCO_MODE_UNDEFINED) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- if (mBluetoothHeadsetDevice != null) {
- mScoAudioMode = Settings.Global.getInt(
- mDeviceBroker.getContentResolver(),
- "bluetooth_sco_channel_"
- + mBluetoothHeadsetDevice.getAddress(),
- SCO_MODE_VIRTUAL_CALL);
- if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- }
+ int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty()
+ ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
+ if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
+ Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
+ + modeOwnerPid + " != creatorPid " + mCreatorPid);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return;
+ }
+ switch (mScoAudioState) {
+ case SCO_STATE_INACTIVE:
+ mScoAudioMode = scoAudioMode;
+ if (scoAudioMode == SCO_MODE_UNDEFINED) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+ if (mBluetoothHeadsetDevice != null) {
+ mScoAudioMode = Settings.Global.getInt(
+ mDeviceBroker.getContentResolver(),
+ "bluetooth_sco_channel_"
+ + mBluetoothHeadsetDevice.getAddress(),
+ SCO_MODE_VIRTUAL_CALL);
+ if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
}
}
- if (mBluetoothHeadset == null) {
- if (getBluetoothHeadset()) {
- mScoAudioState = SCO_STATE_ACTIVATE_REQ;
- } else {
- Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
- + " connection, mScoAudioMode=" + mScoAudioMode);
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- }
- break;
- }
- if (mBluetoothHeadsetDevice == null) {
- Log.w(TAG, "requestScoState: no active device while connecting,"
- + " mScoAudioMode=" + mScoAudioMode);
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- break;
- }
- if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ }
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
} else {
- Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
- + " failed, mScoAudioMode=" + mScoAudioMode);
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " connection, mScoAudioMode=" + mScoAudioMode);
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
break;
- case SCO_STATE_DEACTIVATING:
- mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ }
+ if (mBluetoothHeadsetDevice == null) {
+ Log.w(TAG, "requestScoState: no active device while connecting,"
+ + " mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
break;
- case SCO_STATE_DEACTIVATE_REQ:
+ }
+ if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
- break;
- default:
- Log.w(TAG, "requestScoState: failed to connect in state "
- + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- break;
+ } else {
+ Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
+ + " failed, mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ break;
+ case SCO_STATE_DEACTIVATING:
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to connect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ break;
- }
}
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
switch (mScoAudioState) {
@@ -906,6 +940,9 @@
return null;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
private void clearAllScoClients(int exceptPid, boolean stopSco) {
ScoClient savedClient = null;
for (ScoClient cl : mScoClients) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 258c325..d28482e 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -52,6 +52,7 @@
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.autofill.AutofillManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -159,6 +160,7 @@
private final PackageManager mPm;
private final AppOpsManager mAppOps;
private final ContentCaptureManagerInternal mContentCaptureInternal;
+ private final AutofillManagerInternal mAutofillInternal;
private final IBinder mPermissionOwner;
private HostClipboardMonitor mHostClipboardMonitor = null;
private Thread mHostMonitorThread = null;
@@ -179,6 +181,7 @@
mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);
+ mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
@@ -653,13 +656,18 @@
// Clipboard can only be read by applications with focus..
boolean allowed = mWm.isUidFocused(callingUid);
if (!allowed && mContentCaptureInternal != null) {
- // ...or the Intelligence Service
+ // ...or the Content Capture Service
allowed = mContentCaptureInternal.isContentCaptureServiceForUser(callingUid,
userId);
}
+ if (!allowed && mAutofillInternal != null) {
+ // ...or the Augmented Autofill Service
+ allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(callingUid,
+ userId);
+ }
if (!allowed) {
Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus neither is the IntelligeService for "
+ + ", application is not in focus neither is a system service for "
+ "user " + userId);
}
return allowed;
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 948c690..a1a8e35 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -33,6 +33,7 @@
import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.TrafficStatsConstants;
import libcore.io.IoUtils;
@@ -381,7 +382,8 @@
protected void setupSocket(
int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
throws ErrnoException, IOException {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_PROBE);
try {
mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
} finally {
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 1ac09ad..f6ce2dc 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -39,6 +39,7 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.TrafficStatsConstants;
import com.android.net.IProxyCallback;
import com.android.net.IProxyPortListener;
import com.android.net.IProxyService;
@@ -111,7 +112,8 @@
String file;
final Uri pacUrl = mPacUrl;
if (Uri.EMPTY.equals(pacUrl)) return;
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_PAC);
try {
file = get(pacUrl);
} catch (IOException ioe) {
diff --git a/services/core/java/com/android/server/location/GpsXtraDownloader.java b/services/core/java/com/android/server/location/GpsXtraDownloader.java
index c012ee4..7dffcb4 100644
--- a/services/core/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/core/java/com/android/server/location/GpsXtraDownloader.java
@@ -20,6 +20,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.util.TrafficStatsConstants;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -92,7 +94,8 @@
// load balance our requests among the available servers
while (result == null) {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_GPS);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_GPS);
try {
result = doDownload(mXtraServers[mNextServerIndex]);
} finally {
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
index a5f2217..fdb499b 100644
--- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -140,6 +140,7 @@
private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
// Used to notify System UI and Settings when remote volume was changed.
+ @GuardedBy("mLock")
final RemoteCallbackList<IRemoteVolumeController> mRemoteVolumeControllers =
new RemoteCallbackList<>();
@@ -287,17 +288,19 @@
if (!session.isActive()) {
return;
}
- int size = mRemoteVolumeControllers.beginBroadcast();
- MediaSession.Token token = session.getSessionToken();
- for (int i = size - 1; i >= 0; i--) {
- try {
- IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i);
- cb.remoteVolumeChanged(token, flags);
- } catch (Exception e) {
- Log.w(TAG, "Error sending volume change.", e);
+ synchronized (mLock) {
+ int size = mRemoteVolumeControllers.beginBroadcast();
+ MediaSession.Token token = session.getSessionToken();
+ for (int i = size - 1; i >= 0; i--) {
+ try {
+ IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i);
+ cb.remoteVolumeChanged(token, flags);
+ } catch (Exception e) {
+ Log.w(TAG, "Error sending volume change.", e);
+ }
}
+ mRemoteVolumeControllers.finishBroadcast();
}
- mRemoteVolumeControllers.finishBroadcast();
}
@Override
@@ -647,19 +650,21 @@
return;
}
- int size = mRemoteVolumeControllers.beginBroadcast();
- MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
- MediaSession.Token token = record == null ? null : record.getSessionToken();
+ synchronized (mLock) {
+ int size = mRemoteVolumeControllers.beginBroadcast();
+ MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
+ MediaSession.Token token = record == null ? null : record.getSessionToken();
- for (int i = size - 1; i >= 0; i--) {
- try {
- IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i);
- cb.updateRemoteController(token);
- } catch (Exception e) {
- Log.w(TAG, "Error sending default remote volume.", e);
+ for (int i = size - 1; i >= 0; i--) {
+ try {
+ IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i);
+ cb.updateRemoteController(token);
+ } catch (Exception e) {
+ Log.w(TAG, "Error sending default remote volume.", e);
+ }
}
+ mRemoteVolumeControllers.finishBroadcast();
}
- mRemoteVolumeControllers.finishBroadcast();
}
void pushSession2TokensChangedLocked(int userId) {
@@ -1676,11 +1681,13 @@
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
- try {
- enforceStatusBarServicePermission("listen for volume changes", pid, uid);
- mRemoteVolumeControllers.register(rvc);
- } finally {
- Binder.restoreCallingIdentity(token);
+ synchronized (mLock) {
+ try {
+ enforceStatusBarServicePermission("listen for volume changes", pid, uid);
+ mRemoteVolumeControllers.register(rvc);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
@@ -1689,11 +1696,13 @@
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
- try {
- enforceStatusBarServicePermission("listen for volume changes", pid, uid);
- mRemoteVolumeControllers.unregister(rvc);
- } finally {
- Binder.restoreCallingIdentity(token);
+ synchronized (mLock) {
+ try {
+ enforceStatusBarServicePermission("listen for volume changes", pid, uid);
+ mRemoteVolumeControllers.unregister(rvc);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7f1b25ca..042ac8c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1689,6 +1689,9 @@
mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray(
com.android.internal.R.array.config_nonBlockableNotificationPackages));
+
+ mZenModeHelper.setPriorityOnlyDndExemptPackages(getContext().getResources().getStringArray(
+ com.android.internal.R.array.config_priorityOnlyDndExemptPackages));
}
@Override
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7e74cc2..1f5b99c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -80,6 +80,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -96,7 +97,7 @@
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
- @VisibleForTesting protected final AppOpsManager mAppOps;
+ private final AppOpsManager mAppOps;
@VisibleForTesting protected final NotificationManager mNotificationManager;
@VisibleForTesting protected ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
@@ -123,11 +124,13 @@
@VisibleForTesting protected boolean mIsBootComplete;
+ private String[] mPriorityOnlyDndExemptPackages;
+
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
mContext = context;
mHandler = new H(looper);
addCallback(mMetrics);
- mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mAppOps = context.getSystemService(AppOpsManager.class);
mNotificationManager = context.getSystemService(NotificationManager.class);
mDefaultConfig = readDefaultConfig(mContext.getResources());
@@ -214,6 +217,10 @@
loadConfigForUser(user, "onUserUnlocked");
}
+ void setPriorityOnlyDndExemptPackages(String[] packages) {
+ mPriorityOnlyDndExemptPackages = packages;
+ }
+
private void loadConfigForUser(int user, String reason) {
if (mUser == user || user < UserHandle.USER_SYSTEM) return;
mUser = user;
@@ -994,53 +1001,47 @@
for (int usage : AudioAttributes.SDK_USAGES) {
final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NEVER) {
- applyRestrictions(false /*mute*/, usage);
+ applyRestrictions(zenPriorityOnly, false /*mute*/, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) {
- applyRestrictions(muteNotifications || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteNotifications || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
- applyRestrictions(muteCalls || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteCalls || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) {
- applyRestrictions(muteAlarms || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteAlarms || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA) {
- applyRestrictions(muteMedia || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteMedia || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_SYSTEM) {
if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) {
// normally DND will only restrict touch sounds, not haptic feedback/vibrations
- applyRestrictions(muteSystem || muteEverything, usage,
+ applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage,
AppOpsManager.OP_PLAY_AUDIO);
- applyRestrictions(false, usage, AppOpsManager.OP_VIBRATE);
+ applyRestrictions(zenPriorityOnly, false, usage, AppOpsManager.OP_VIBRATE);
} else {
- applyRestrictions(muteSystem || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage);
}
} else {
- applyRestrictions(muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteEverything, usage);
}
}
}
@VisibleForTesting
- protected void applyRestrictions(boolean mute, int usage, int code) {
- final String[] exceptionPackages = null; // none (for now)
-
- // Only do this if we are executing within the system process... otherwise
- // we are running as test code, so don't have access to the protected call.
- if (Process.myUid() == Process.SYSTEM_UID) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mAppOps.setRestriction(code, usage,
- mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage, int code) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mAppOps.setRestriction(code, usage,
+ mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ zenPriorityOnly ? mPriorityOnlyDndExemptPackages : null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@VisibleForTesting
- protected void applyRestrictions(boolean mute, int usage) {
- applyRestrictions(mute, usage, AppOpsManager.OP_VIBRATE);
- applyRestrictions(mute, usage, AppOpsManager.OP_PLAY_AUDIO);
+ protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage) {
+ applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_VIBRATE);
+ applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_PLAY_AUDIO);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c12ee03..c4d4106 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2801,6 +2801,11 @@
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
+ } else {
+ // We're expecting that the system app should remain disabled, but add
+ // it to expecting better to recover in case the data version cannot
+ // be scanned.
+ mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
}
}
}
@@ -5733,6 +5738,8 @@
"getWhitelistedRestrictedPermissions for user " + userId);
}
+ final PackageParser.Package pkg;
+
synchronized (mPackages) {
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
if (packageSetting == null) {
@@ -5740,6 +5747,8 @@
return null;
}
+ pkg = packageSetting.pkg;
+
final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
== PackageManager.PERMISSION_GRANTED;
@@ -5767,14 +5776,14 @@
UserHandle.getCallingUserId())) {
return null;
}
+ }
- final long identity = Binder.clearCallingIdentity();
- try {
- return mPermissionManager.getWhitelistedRestrictedPermissions(
- packageSetting.pkg, whitelistFlags, userId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mPermissionManager.getWhitelistedRestrictedPermissions(
+ pkg, whitelistFlags, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -5785,6 +5794,10 @@
// Other argument checks are done in get/setWhitelistedRestrictedPermissions
Preconditions.checkNotNull(permission);
+ if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) {
+ return false;
+ }
+
List<String> permissions = getWhitelistedRestrictedPermissions(packageName,
whitelistFlags, userId);
if (permissions == null) {
@@ -5792,13 +5805,31 @@
}
if (permissions.indexOf(permission) < 0) {
permissions.add(permission);
- setWhitelistedRestrictedPermissions(packageName, permissions,
+ return setWhitelistedRestrictedPermissions(packageName, permissions,
whitelistFlags, userId);
- return true;
}
return false;
}
+ private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(
+ @NonNull String permission) {
+ synchronized (mPackages) {
+ final BasePermission bp = mPermissionManager.getPermissionTEMP(permission);
+ if (bp == null) {
+ Slog.w(TAG, "No such permissions: " + permission);
+ return false;
+ }
+ if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted()
+ && mContext.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Cannot modify whitelisting of an immutably "
+ + "restricted permission: " + permission);
+ }
+ return true;
+ }
+ }
+
@Override
public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
@NonNull String permission, @PermissionWhitelistFlags int whitelistFlags,
@@ -5806,17 +5837,20 @@
// Other argument checks are done in get/setWhitelistedRestrictedPermissions
Preconditions.checkNotNull(permission);
+ if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) {
+ return false;
+ }
+
final List<String> permissions = getWhitelistedRestrictedPermissions(packageName,
whitelistFlags, userId);
if (permissions != null && permissions.remove(permission)) {
- setWhitelistedRestrictedPermissions(packageName, permissions,
+ return setWhitelistedRestrictedPermissions(packageName, permissions,
whitelistFlags, userId);
- return true;
}
return false;
}
- private void setWhitelistedRestrictedPermissions(@NonNull String packageName,
+ private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName,
@Nullable List<String> permissions, @PermissionWhitelistFlags int whitelistFlag,
@UserIdInt int userId) {
Preconditions.checkNotNull(packageName);
@@ -5833,13 +5867,17 @@
"setWhitelistedRestrictedPermissions for user " + userId);
}
+ final PackageParser.Package pkg;
+
synchronized (mPackages) {
final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
if (packageSetting == null) {
Slog.w(TAG, "Unknown package: " + packageName);
- return;
+ return false;
}
+ pkg = packageSetting.pkg;
+
final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
== PackageManager.PERMISSION_GRANTED;
@@ -5864,7 +5902,7 @@
packageName, whitelistFlag, userId);
if (permissions == null || permissions.isEmpty()) {
if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) {
- return;
+ return true;
}
} else {
// Only the system can add and remove while the installer can only remove.
@@ -5890,18 +5928,20 @@
if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(),
UserHandle.getCallingUserId())) {
- return;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- mPermissionManager.setWhitelistedRestrictedPermissions(packageSetting.pkg,
- new int[]{userId}, permissions, Process.myUid(), whitelistFlag,
- mPermissionCallback);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ return false;
}
}
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mPermissionManager.setWhitelistedRestrictedPermissions(pkg,
+ new int[]{userId}, permissions, Process.myUid(), whitelistFlag,
+ mPermissionCallback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return true;
}
@Override
@@ -9334,6 +9374,7 @@
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (scanResult.success) {
synchronized (mPackages) {
+ boolean appIdCreated = false;
try {
final String pkgName = scanResult.pkgSetting.name;
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
@@ -9346,11 +9387,12 @@
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
- prepareScanResultLocked(scanResult);
- commitReconciledScanResultLocked(
- reconcileResult.get(pkgName));
+ appIdCreated = optimisticallyRegisterAppId(scanResult);
+ commitReconciledScanResultLocked(reconcileResult.get(pkgName));
} catch (PackageManagerException e) {
- unprepareScanResultLocked(scanResult);
+ if (appIdCreated) {
+ cleanUpAppIdCreation(scanResult);
+ }
throw e;
}
}
@@ -10837,27 +10879,32 @@
}
- /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
- private void prepareScanResultLocked(@NonNull ScanResult result)
+ /**
+ * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
+ * the app ID required for reconcile.
+ * @return {@code true} if a new app ID was registered and will need to be cleaned up on
+ * failure.
+ */
+ private boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
throws PackageManagerException {
if (!result.existingSettingCopied) {
// THROWS: when we can't allocate a user id. add call to check if there's
// enough space to ensure we won't throw; otherwise, don't modify state
- mSettings.registerAppIdLPw(result.pkgSetting);
+ return mSettings.registerAppIdLPw(result.pkgSetting);
}
+ return false;
}
/**
- * Reverts any changes to the system that were made by
- * {@link #prepareScanResultLocked(ScanResult)}
+ * Reverts any app ID creation that were made by
+ * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
+ * referenced method returned true.
*/
- private void unprepareScanResultLocked(@NonNull ScanResult result) {
- if (!result.existingSettingCopied) {
- // iff we've acquired an app ID for a new package setting, remove it so that it can be
- // acquired by another request.
- if (result.pkgSetting.appId > 0) {
- mSettings.removeAppIdLPw(result.pkgSetting.appId);
- }
+ private void cleanUpAppIdCreation(@NonNull ScanResult result) {
+ // iff we've acquired an app ID for a new package setting, remove it so that it can be
+ // acquired by another request.
+ if (result.pkgSetting.appId > 0) {
+ mSettings.removeAppIdLPw(result.pkgSetting.appId);
}
}
@@ -14608,6 +14655,20 @@
return mUser;
}
+ /**
+ * Gets the user handle for the user that the rollback agent should
+ * use to look up information about this installation when enabling
+ * rollback.
+ */
+ UserHandle getRollbackUser() {
+ // The session for packages installed for "all" users is
+ // associated with the "system" user.
+ if (mUser == UserHandle.ALL) {
+ return UserHandle.SYSTEM;
+ }
+ return mUser;
+ }
+
HandlerParams setTraceMethod(String traceMethod) {
this.traceMethod = traceMethod;
return this;
@@ -15226,7 +15287,7 @@
installedUsers);
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER,
- getUser().getIdentifier());
+ getRollbackUser().getIdentifier());
enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -16701,6 +16762,7 @@
final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
final Map<String, PackageSetting> lastStaticSharedLibSettings =
new ArrayMap<>(requests.size());
+ final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
@@ -16740,7 +16802,7 @@
+ " in multi-package install request.");
return;
}
- prepareScanResultLocked(result);
+ createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.pkgSetting.pkg.packageName,
getSettingsVersionForPackage(result.pkgSetting.pkg));
if (result.staticSharedLibraryInfo != null) {
@@ -16797,7 +16859,9 @@
} finally {
if (!success) {
for (ScanResult result : preparedScans.values()) {
- unprepareScanResultLocked(result);
+ if (createdAppId.getOrDefault(result.request.pkg.packageName, false)) {
+ cleanUpAppIdCreation(result);
+ }
}
}
for (PrepareResult result : prepareResults.values()) {
@@ -19890,26 +19954,28 @@
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"replace preferred activity");
- synchronized (mPackages) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
- != PackageManager.PERMISSION_GRANTED) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ synchronized (mPackages) {
if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
+ Binder.getCallingUid());
return;
}
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+ }
- PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
+ synchronized (mPackages) {
+ final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
if (pir != null) {
// Get all of the existing entries that exactly match this filter.
- ArrayList<PreferredActivity> existing = pir.findFilters(filter);
+ final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
if (existing != null && existing.size() == 1) {
- PreferredActivity cur = existing.get(0);
+ final PreferredActivity cur = existing.get(0);
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Checking replace of preferred:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
@@ -19939,14 +20005,13 @@
return;
}
}
-
if (existing != null) {
if (DEBUG_PREFERRED) {
Slog.i(TAG, existing.size() + " existing preferred matches for:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
- for (int i = 0; i < existing.size(); i++) {
- PreferredActivity pa = existing.get(i);
+ for (int i = existing.size() - 1; i >= 0; --i) {
+ final PreferredActivity pa = existing.get(i);
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Removing existing preferred activity "
+ pa.mPref.mComponent + ":");
@@ -19956,9 +20021,9 @@
}
}
}
- addPreferredActivityInternal(filter, match, set, activity, true, userId,
- "Replacing preferred");
}
+ addPreferredActivityInternal(filter, match, set, activity, true, userId,
+ "Replacing preferred");
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4e4a0e4..6b804df 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2423,6 +2423,16 @@
sessionParams.setStaged();
break;
case "--enable-rollback":
+ if (params.installerPackageName == null) {
+ // com.android.shell has the TEST_MANAGE_ROLLBACKS
+ // permission needed to enable rollback for non-module
+ // packages, which is likely what the user wants when
+ // enabling rollback through the shell command. Set
+ // the installer to com.android.shell if no installer
+ // has been provided so that the user doesn't have to
+ // remember to set it themselves.
+ params.installerPackageName = "com.android.shell";
+ }
sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
break;
default:
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 41a8a77..db2fba9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -810,15 +810,20 @@
/**
* Registers a user ID with the system. Potentially allocates a new user ID.
+ * @return {@code true} if a new app ID was created in the process. {@code false} can be
+ * returned in the case that a shared user ID already exists or the explicit app ID is
+ * already registered.
* @throws PackageManagerException If a user ID could not be allocated.
*/
- void registerAppIdLPw(PackageSetting p) throws PackageManagerException {
+ boolean registerAppIdLPw(PackageSetting p) throws PackageManagerException {
+ final boolean createdNew;
if (p.appId == 0) {
// Assign new user ID
p.appId = acquireAndRegisterNewAppIdLPw(p);
+ createdNew = true;
} else {
// Add new setting to list of user IDs
- registerExistingAppIdLPw(p.appId, p, p.name);
+ createdNew = registerExistingAppIdLPw(p.appId, p, p.name);
}
if (p.appId < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -826,6 +831,7 @@
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Package " + p.name + " could not be assigned a valid UID");
}
+ return createdNew;
}
/**
@@ -4413,7 +4419,7 @@
ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION",
ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE",
ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, "ALLOW_AUDIO_PLAYBACK_CAPTURE",
- ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, "ALLOW_EXTERNAL_STORAGE_SANDBOX",
+ ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE, "PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE",
ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
@@ -4737,7 +4743,7 @@
}
pw.print(prefix); pw.print(" "); pw.print(perm);
final BasePermission bp = mPermissions.getPermission(perm);
- if (bp != null && bp.isRestricted()) {
+ if (bp != null && bp.isHardOrSoftRestricted()) {
pw.println(": restricted=true");
} else {
pw.println();
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 490c647..6d22faa 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -203,12 +203,17 @@
&& (perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
}
- public boolean isRestricted() {
+ public boolean isHardOrSoftRestricted() {
return perm != null && perm.info != null
&& (perm.info.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
| PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
}
+ public boolean isImmutablyRestricted() {
+ return perm != null && perm.info != null
+ && (perm.info.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
+ }
+
public boolean isSignature() {
return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
PermissionInfo.PROTECTION_SIGNATURE;
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 dd63e3c..9ede263 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2059,7 +2059,7 @@
return;
}
- if (RESTRICTED_PERMISSIONS_ENABLED && bp.isRestricted()
+ if (RESTRICTED_PERMISSIONS_ENABLED && bp.isHardOrSoftRestricted()
&& (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
Log.e(TAG, "Cannot grant restricted non-exempt permission "
+ permName + " for package " + packageName);
@@ -2241,7 +2241,7 @@
continue;
}
- if (!bp.isRestricted()) {
+ if (!bp.isHardOrSoftRestricted()) {
continue;
}
@@ -2294,13 +2294,16 @@
updatePermissions = true;
+ final boolean wasWhitelisted = (oldFlags
+ & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+ final boolean isWhitelisted = (newFlags
+ & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+
// If the permission is policy fixed as granted but it is no longer
// on any of the whitelists we need to clear the policy fixed flag
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isWhitelisted = (newFlags
- & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
final boolean isGranted = permissionsState.hasPermission(permissionName, userId);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -2308,6 +2311,14 @@
}
}
+ // If we are whitelisting an app that does not support runtime permissions
+ // we need to make sure it goes through the permission review UI at launch.
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && !wasWhitelisted && isWhitelisted) {
+ mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
updatePermissionFlags(permissionName, pkg.packageName, mask, newFlags,
callingUid, userId, false, null /*callback*/);
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 1fd8b71..a280d83 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -36,9 +37,7 @@
import android.os.UserHandle;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManagerInternal;
-import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.server.FgThread;
@@ -46,7 +45,6 @@
import com.android.server.SystemService;
import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -57,17 +55,10 @@
* and app ops - and vise versa.
*/
public final class PermissionPolicyService extends SystemService {
- private static final String PLATFORM_PACKAGE = "android";
-
private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName();
- // No need to lock as this is populated on boot when the OS is
- // single threaded and is never mutated until a reboot.
- private static final ArraySet<String> sAllRestrictedPermissions = new ArraySet<>();
-
public PermissionPolicyService(@NonNull Context context) {
super(context);
- cacheAllRestrictedPermissions(context);
}
@Override
@@ -101,20 +92,6 @@
startWatchingRuntimePermissionChanges(getContext(), userId);
}
- private static void cacheAllRestrictedPermissions(@NonNull Context context) {
- try {
- final PackageInfo packageInfo = context.getPackageManager()
- .getPackageInfo(PLATFORM_PACKAGE, PackageManager.GET_PERMISSIONS);
- for (PermissionInfo permissionInfo : packageInfo.permissions) {
- if (permissionInfo.isRestricted()) {
- sAllRestrictedPermissions.add(permissionInfo.name);
- }
- }
- } catch (NameNotFoundException impossible) {
- /* cannot happen */
- }
- }
-
private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context,
@UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -162,7 +139,7 @@
}
private static @Nullable Context getUserContext(@NonNull Context context,
- @NonNull UserHandle user) {
+ @Nullable UserHandle user) {
if (context.getUser().equals(user)) {
return context;
} else {
@@ -230,18 +207,31 @@
private final @NonNull SparseIntArray mAllUids = new SparseIntArray();
/**
- * All ops that need to be restricted
+ * All ops that need to be set to default
+ *
+ * Currently, only used by the restricted permissions logic.
*
* @see #syncRestrictedOps
*/
- private final @NonNull ArrayList<OpToRestrict> mOpsToRestrict = new ArrayList<>();
+ private final @NonNull ArrayList<OpToRestrict> mOpsToDefault = new ArrayList<>();
/**
- * All ops that need to be unrestricted
+ * All ops that need to be flipped to allow if default.
+ *
+ * Currently, only used by the restricted permissions logic.
*
* @see #syncRestrictedOps
*/
- private final @NonNull ArrayList<OpToUnrestrict> mOpsToUnrestrict = new ArrayList<>();
+ private final @NonNull ArrayList<OpToUnrestrict> mOpsToAllow = new ArrayList<>();
+
+ /**
+ * All ops that need to be flipped to ignore if default.
+ *
+ * Currently, only used by the restricted permissions logic.
+ *
+ * @see #syncRestrictedOps
+ */
+ private final @NonNull ArrayList<OpToUnrestrict> mOpsToIgnore = new ArrayList<>();
/**
* All foreground permissions
@@ -262,89 +252,20 @@
* <p>This processes ops previously added by {@link #addOpIfRestricted}
*/
private void syncRestrictedOps() {
- final SparseIntArray unprocessedUids = mAllUids.clone();
-
- // TRICKY: we set the app op for a restricted permission to allow if the app
- // requesting the permission is whitelisted and to deny if the app requesting
- // the permission is not whitelisted. However, there is another case where an
- // app in a shared user can access a component in another app in the same shared
- // user due to being in the same shared user and not by having the permission
- // that guards the component form the rest of the world. We need to handle this.
- // The way we do this is by setting app ops corresponding to non requested
- // restricted permissions to allow as this would allow the shared uid access
- // case and be okay for other apps as they would not have the permission and
- // would fail on the permission checks before reaching the app op check.
- final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid =
- new SparseArray<>();
-
- final int unrestrictCount = mOpsToUnrestrict.size();
- for (int i = 0; i < unrestrictCount; i++) {
- final OpToUnrestrict op = mOpsToUnrestrict.get(i);
- setUidModeAllowed(op.code, op.uid, op.packageName);
-
- // Keep track this permission was requested by the UID.
- List<String> unrequestedRestrictedPermissions =
- unrequestedRestrictedPermissionsForUid.get(op.uid);
- if (unrequestedRestrictedPermissions == null) {
- unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
- unrequestedRestrictedPermissionsForUid.put(op.uid,
- unrequestedRestrictedPermissions);
- }
- unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code));
-
- unprocessedUids.delete(op.uid);
+ final int allowCount = mOpsToAllow.size();
+ for (int i = 0; i < allowCount; i++) {
+ final OpToUnrestrict op = mOpsToAllow.get(i);
+ setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
}
- final int restrictCount = mOpsToRestrict.size();
- for (int i = 0; i < restrictCount; i++) {
- final OpToRestrict op = mOpsToRestrict.get(i);
+ final int ignoreCount = mOpsToIgnore.size();
+ for (int i = 0; i < ignoreCount; i++) {
+ final OpToUnrestrict op = mOpsToIgnore.get(i);
+ setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+ }
+ final int defaultCount = mOpsToDefault.size();
+ for (int i = 0; i < defaultCount; i++) {
+ final OpToRestrict op = mOpsToDefault.get(i);
setUidModeDefault(op.code, op.uid);
-
- // Keep track this permission was requested by the UID.
- List<String> unrequestedRestrictedPermissions =
- unrequestedRestrictedPermissionsForUid.get(op.uid);
- if (unrequestedRestrictedPermissions == null) {
- unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
- unrequestedRestrictedPermissionsForUid.put(op.uid,
- unrequestedRestrictedPermissions);
- }
- unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code));
-
- unprocessedUids.delete(op.uid);
- }
-
- // Give root access
- unprocessedUids.put(Process.ROOT_UID, Process.ROOT_UID);
-
- // Add records for UIDs that don't use any restricted permissions.
- final int uidCount = unprocessedUids.size();
- for (int i = 0; i < uidCount; i++) {
- final int uid = unprocessedUids.keyAt(i);
- unrequestedRestrictedPermissionsForUid.put(uid,
- new ArrayList<>(sAllRestrictedPermissions));
- }
-
- // Flip ops for all unrequested restricted permission for the UIDs.
- final int unrequestedUidCount = unrequestedRestrictedPermissionsForUid.size();
- for (int i = 0; i < unrequestedUidCount; i++) {
- final List<String> unrequestedRestrictedPermissions =
- unrequestedRestrictedPermissionsForUid.valueAt(i);
- if (unrequestedRestrictedPermissions != null) {
- final int uid = unrequestedRestrictedPermissionsForUid.keyAt(i);
- final String[] packageNames = (uid != Process.ROOT_UID)
- ? mPackageManager.getPackagesForUid(uid)
- : new String[] {"root"};
- if (packageNames == null) {
- continue;
- }
- final int permissionCount = unrequestedRestrictedPermissions.size();
- for (int j = 0; j < permissionCount; j++) {
- final String permission = unrequestedRestrictedPermissions.get(j);
- for (String packageName : packageNames) {
- setUidModeAllowed(AppOpsManager.permissionToOpCode(permission), uid,
- packageName);
- }
- }
- }
}
}
@@ -411,12 +332,27 @@
if (permissionInfo.isHardRestricted()) {
if (applyRestriction) {
- mOpsToRestrict.add(new OpToRestrict(uid, opCode));
+ mOpsToDefault.add(new OpToRestrict(uid, opCode));
} else {
- mOpsToUnrestrict.add(new OpToUnrestrict(uid, pkg.packageName, opCode));
+ mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, opCode));
}
} else if (permissionInfo.isSoftRestricted()) {
- //TODO: Implement soft restrictions like storage here.
+ // Storage uses a special app op to decide the mount state and
+ // supports soft restriction where the restricted state allows
+ // the permission but only for accessing the medial collections.
+ if (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission)
+ || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
+ if (applyRestriction) {
+ mOpsToDefault.add(new OpToRestrict(uid,
+ AppOpsManager.OP_LEGACY_STORAGE));
+ } else if (pkg.applicationInfo.hasRequestedLegacyExternalStorage()) {
+ mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName,
+ AppOpsManager.OP_LEGACY_STORAGE));
+ } else {
+ mOpsToIgnore.add(new OpToUnrestrict(uid, pkg.packageName,
+ AppOpsManager.OP_LEGACY_STORAGE));
+ }
+ }
}
}
@@ -474,11 +410,20 @@
}
}
- private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
+ private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) {
+ setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_ALLOWED, packageName);
+ }
+
+ private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) {
+ setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_IGNORED, packageName);
+ }
+
+ private void setUidModeIfDefault(int opCode, int uid, int mode,
+ @NonNull String packageName) {
final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
.opToPublicName(opCode), uid, packageName);
if (currentMode == AppOpsManager.MODE_DEFAULT) {
- mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED);
+ mAppOpsManager.setUidMode(opCode, uid, mode);
}
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 0381445..1552fd5 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -312,10 +312,8 @@
if (!mThermalEventListeners.register(listener, null)) {
return false;
}
- if (mHalReady.get()) {
- // Notify its callback after new client registered.
- postEventListenerCurrentTemperatures(listener, null);
- }
+ // Notify its callback after new client registered.
+ postEventListenerCurrentTemperatures(listener, null);
return true;
} finally {
Binder.restoreCallingIdentity(token);
@@ -334,10 +332,8 @@
if (!mThermalEventListeners.register(listener, new Integer(type))) {
return false;
}
- if (mHalReady.get()) {
- // Notify its callback after new client registered.
- postEventListenerCurrentTemperatures(listener, new Integer(type));
- }
+ // Notify its callback after new client registered.
+ postEventListenerCurrentTemperatures(listener, new Integer(type));
return true;
} finally {
Binder.restoreCallingIdentity(token);
@@ -398,10 +394,8 @@
if (!mThermalStatusListeners.register(listener)) {
return false;
}
- if (mHalReady.get()) {
- // Notify its callback after new client registered.
- postStatusListener(listener);
- }
+ // Notify its callback after new client registered.
+ postStatusListener(listener);
return true;
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index b2ac1b8..654c477 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -147,6 +147,8 @@
mLegacyRoleResolver = legacyRoleResolver;
+ RoleControllerManager.initializeRemoteServiceComponentName(context);
+
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
@@ -231,7 +233,7 @@
// Run grants again
Slog.i(LOG_TAG, "Granting default permissions...");
CompletableFuture<Void> result = new CompletableFuture<>();
- getOrCreateControllerService(userId).grantDefaultRoles(FgThread.getExecutor(),
+ getOrCreateController(userId).grantDefaultRoles(FgThread.getExecutor(),
successful -> {
if (successful) {
userState.setPackagesHash(packagesHash);
@@ -318,7 +320,7 @@
}
@NonNull
- private RoleControllerManager getOrCreateControllerService(@UserIdInt int userId) {
+ private RoleControllerManager getOrCreateController(@UserIdInt int userId) {
synchronized (mLock) {
RoleControllerManager controller = mControllers.get(userId);
if (controller == null) {
@@ -330,7 +332,8 @@
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
- controller = new RoleControllerManager(context, FgThread.getHandler());
+ controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName(
+ FgThread.getHandler(), context);
mControllers.put(userId, controller);
}
return controller;
@@ -474,7 +477,7 @@
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
- getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, flags,
+ getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags,
callback);
}
@@ -494,7 +497,7 @@
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
- getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName, flags,
+ getOrCreateController(userId).onRemoveRoleHolder(roleName, packageName, flags,
callback);
}
@@ -513,7 +516,7 @@
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkNotNull(callback, "callback cannot be null");
- getOrCreateControllerService(userId).onClearRoleHolders(roleName, flags, callback);
+ getOrCreateController(userId).onClearRoleHolders(roleName, flags, callback);
}
@Override
@@ -738,10 +741,10 @@
}
});
if (packageName != null) {
- getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
+ getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
packageName, 0, callback);
} else {
- getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
+ getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
callback);
}
try {
@@ -762,10 +765,10 @@
}
});
if (packageName != null) {
- getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
+ getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
packageName, 0, callback);
} else {
- getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
+ getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
callback);
}
}
@@ -791,10 +794,10 @@
callback.accept(successful);
});
if (packageName != null) {
- getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME,
+ getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_HOME,
packageName, 0, remoteCallback);
} else {
- getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0,
+ getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0,
remoteCallback);
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 44ed070..da9bc16 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -64,6 +64,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.CoolingDevice;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -1798,6 +1799,28 @@
e.writeInt(temp.getType());
e.writeString(temp.getName());
e.writeInt((int) (temp.getValue() * 10));
+ e.writeInt(temp.getStatus());
+ pulledData.add(e);
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices();
+ for (CoolingDevice device : devices) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(device.getType());
+ e.writeString(device.getName());
+ e.writeInt((int) (device.getValue()));
pulledData.add(e);
}
} catch (RemoteException e) {
@@ -2271,6 +2294,10 @@
pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.COOLING_DEVICE: {
+ pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.DEBUG_ELAPSED_CLOCK: {
pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
break;
@@ -2535,12 +2562,9 @@
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
public void notifyThrottling(Temperature temp) {
- boolean isThrottling = temp.getStatus() >= Temperature.THROTTLING_SEVERE;
StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(),
- isThrottling ?
- StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__START :
- StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__STOP,
- temp.getValue());
+ StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__UNKNOWN,
+ temp.getValue(), temp.getStatus(), temp.getName());
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ea9477f..4ef8753 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -971,8 +971,9 @@
: (realCallingUid == Process.SYSTEM_UID)
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (realCallingUid != callingUid) {
- // don't abort if the realCallingUid has a visible window
- if (realCallingUidHasAnyVisibleWindow) {
+ // don't abort if the realCallingUid has a visible window, unless realCallingUid is
+ // SYSTEM_UID, in which case it start needs to be explicitly whitelisted
+ if (realCallingUidHasAnyVisibleWindow && realCallingUid != Process.SYSTEM_UID) {
return false;
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index c1d872f..b5e7067 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -435,6 +435,18 @@
moveFromFullscreen, moveToFullscreen, animationType);
}
+ /**
+ * Cancel existing animation if the destination was modified.
+ */
+ void cancel(final BoundsAnimationTarget target) {
+ final BoundsAnimator existing = mRunningAnimations.get(target);
+ if (existing != null) {
+ // Cancel animation. Since its already started, send animation end to client.
+ if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target);
+ existing.cancelAndCallAnimationEnd();
+ }
+ }
+
@VisibleForTesting
BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f9fd541..9f42324 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -312,11 +313,12 @@
/**
* Returns true if the callingUid has any non-toast window currently visible to the user.
+ * Also ignores TYPE_APPLICATION_STARTING, since those windows don't belong to apps.
*/
boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
return forAllWindows(w ->
w.getOwningUid() == callingUid && w.mAttrs.type != TYPE_TOAST
- && w.isVisibleNow(),
+ && w.mAttrs.type != TYPE_APPLICATION_STARTING && w.isVisibleNow(),
true /* traverseTopToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bdb4d04..7515b3f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -388,7 +388,9 @@
* @return true if bounds were updated to some non-empty value.
*/
boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) {
+ boolean animating = false;
if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
+ animating = true;
getFinalAnimationBounds(mTmpRect2);
} else {
mTmpRect2.set(inOutBounds);
@@ -398,6 +400,13 @@
if (updated) {
inOutBounds.set(mTmpRect3);
+ // The final boundary is updated while there is an existing boundary animation. Let's
+ // cancel this animation to prevent the obsolete animation overwritten updated bounds.
+ if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) {
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.mBoundsAnimationController.getHandler().post(() ->
+ displayContent.mBoundsAnimationController.cancel(this));
+ }
// Once we've set the bounds based on the rotation of the old bounds in the new
// orientation, clear the animation target bounds since they are obsolete, and
// cancel any currently running animations
@@ -1585,7 +1594,6 @@
mBoundsAnimatingRequested = false;
mBoundsAnimating = true;
- mCancelCurrentBoundsAnimation = false;
mAnimationType = animationType;
// If we are changing UI mode, as in the PiP to fullscreen
@@ -1645,7 +1653,7 @@
mBoundsAnimationTarget, false /* forceUpdate */);
}
- if (finalStackSize != null) {
+ if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
setPinnedStackSize(finalStackSize, null);
} else {
// We have been canceled, so the final stack size is null, still run the
@@ -1758,6 +1766,7 @@
}
final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
+ mCancelCurrentBoundsAnimation = false;
displayContent.mBoundsAnimationController.getHandler().post(() -> {
displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
finalToBounds, animationDuration, finalSchedulePipModeChangedState,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0cd730b..2a2de77 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -32,7 +32,6 @@
import android.content.Intent;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
@@ -1248,11 +1247,6 @@
mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS);
traceEnd();
- // NOTE: ClipboardService indirectly depends on IntelligenceService
- traceBeginAndSlog("StartClipboardService");
- mSystemServiceManager.startService(ClipboardService.class);
- traceEnd();
-
traceBeginAndSlog("InitNetworkStackClient");
try {
NetworkStackClient.getInstance().init();
@@ -1887,6 +1881,11 @@
traceEnd();
}
+ // NOTE: ClipboardService depends on ContentCapture and Autofill
+ traceBeginAndSlog("StartClipboardService");
+ mSystemServiceManager.startService(ClipboardService.class);
+ traceEnd();
+
traceBeginAndSlog("AppServiceManager");
mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
traceEnd();
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 339607b..59aea21 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -36,6 +36,7 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.TrafficStatsConstants;
import libcore.io.IoBridge;
@@ -586,7 +587,8 @@
private boolean createSocket() {
final int SEND_TIMEOUT_MS = 300;
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NEIGHBOR);
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(
+ TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
try {
mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
// Setting SNDTIMEO is purely for defensive purposes.
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index c8d1eb4..74fe81c 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@@ -38,6 +39,7 @@
import android.app.backup.BackupManager;
import android.app.backup.IBackupObserver;
import android.app.backup.ISelectBackupTransportCallback;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
@@ -48,6 +50,7 @@
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.PowerSaveState;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
@@ -66,6 +69,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -1130,6 +1134,31 @@
/* transportManager */ null));
}
+ /**
+ * Test verifying that creating a new instance registers the broadcast receiver for package
+ * tracking
+ */
+ @Test
+ public void testCreateAndInitializeService_registersPackageTrackingReceiver() throws Exception {
+ Context contextSpy = Mockito.spy(mContext);
+
+ UserBackupManagerService service = UserBackupManagerService.createAndInitializeService(
+ USER_ID,
+ contextSpy,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+
+ BroadcastReceiver packageTrackingReceiver = service.getPackageTrackingReceiver();
+ assertThat(packageTrackingReceiver).isNotNull();
+
+ // One call for package changes and one call for sd card events.
+ verify(contextSpy, times(2)).registerReceiverAsUser(
+ eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any());
+ }
+
private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 164570a..cc64323 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -86,6 +86,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.ConditionVariable;
import android.os.DeadObjectException;
@@ -100,6 +101,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
@@ -116,7 +118,6 @@
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
-import com.android.server.testing.shadows.ShadowBackupActivityThread;
import com.android.server.testing.shadows.ShadowBackupDataInput;
import com.android.server.testing.shadows.ShadowBackupDataOutput;
import com.android.server.testing.shadows.ShadowEventLog;
@@ -163,8 +164,7 @@
ShadowBackupDataInput.class,
ShadowBackupDataOutput.class,
ShadowEventLog.class,
- ShadowQueuedWork.class,
- ShadowBackupActivityThread.class,
+ ShadowQueuedWork.class
})
@Presubmit
public class KeyValueBackupTaskTest {
@@ -179,6 +179,7 @@
@Mock private IBackupObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
@Mock private OnTaskFinishedListener mListener;
+ @Mock private PackageManagerInternal mPackageManagerInternal;
private UserBackupManagerService mBackupManagerService;
private TransportData mTransport;
private ShadowLooper mShadowBackupLooper;
@@ -243,6 +244,11 @@
mShadowBackupLooper = shadowOf(mBackupHandler.getLooper());
ShadowEventLog.setUp();
mReporter = spy(new KeyValueBackupReporter(mBackupManagerService, mObserver, mMonitor));
+
+ when(mPackageManagerInternal.getApplicationEnabledState(any(), anyInt()))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
}
@After
@@ -471,7 +477,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
runTask(task);
@@ -484,7 +490,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task =
createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE);
@@ -498,7 +504,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1);
runTask(task);
@@ -1307,7 +1313,7 @@
argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
.then(copyBackupDataTo(backupDataPath));
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
agentOnBackupDo(
pmAgent,
(oldState, dataOutput, newState) -> {
@@ -1371,7 +1377,7 @@
setUpAgent(PACKAGE_1);
when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
agentOnBackupDo(
pmAgent,
(oldState, dataOutput, newState) -> {
@@ -1395,7 +1401,7 @@
setUpAgent(PACKAGE_1);
when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
agentOnBackupDo(
pmAgent,
(oldState, dataOutput, newState) -> {
@@ -1957,7 +1963,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -1970,7 +1976,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -1983,7 +1989,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
@@ -1996,7 +2002,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
agentOnBackupDo(
pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
@@ -2011,7 +2017,7 @@
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgent(PACKAGE_1);
BackupAgent pmAgent = spy(createPmAgent());
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent();
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
agentOnBackupDo(
pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java
deleted file mode 100644
index ca2e3b6..0000000
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 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.testing.shadows;
-
-import android.app.ActivityThread;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowActivityThread;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import javax.annotation.Nonnull;
-
-/**
- * Extends the existing {@link ShadowActivityThread} to add support for
- * {@link PackageManager#getApplicationEnabledSetting(String)} in the shadow {@link PackageManager}
- * returned by {@link ShadowBackupActivityThread#getPackageManager()}.
- */
-@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true)
-public class ShadowBackupActivityThread extends ShadowActivityThread {
- @Implementation
- public static Object getPackageManager() {
- ClassLoader classLoader = ShadowActivityThread.class.getClassLoader();
- Class<?> iPackageManagerClass;
- try {
- iPackageManagerClass = classLoader.loadClass("android.content.pm.IPackageManager");
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- return Proxy.newProxyInstance(
- classLoader,
- new Class[] {iPackageManagerClass},
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, @Nonnull Method method, Object[] args)
- throws Exception {
- if (method.getName().equals("getApplicationInfo")) {
- String packageName = (String) args[0];
- int flags = (Integer) args[1];
-
- try {
- return RuntimeEnvironment.application
- .getPackageManager()
- .getApplicationInfo(packageName, flags);
- } catch (PackageManager.NameNotFoundException e) {
- throw new RemoteException(e.getMessage());
- }
- } else if (method.getName().equals("getApplicationEnabledSetting")) {
- return 0;
- } else {
- return null;
- }
- }
- });
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
new file mode 100644
index 0000000..8426a0b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 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.attention;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.service.attention.IAttentionCallback;
+import android.service.attention.IAttentionService;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.attention.AttentionManagerService.AttentionCheck;
+import com.android.server.attention.AttentionManagerService.AttentionHandler;
+import com.android.server.attention.AttentionManagerService.UserState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.attention.AttentionManagerService}
+ */
+@SmallTest
+public class AttentionManagerServiceTest {
+ private AttentionManagerService mSpyAttentionManager;
+ private UserState mSpyUserState;
+ private final int mTimeout = 1000;
+ @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal;
+ @Mock private AttentionHandler mMockHandler;
+ @Mock private IAttentionCallback mMockIAttentionCallback;
+ @Mock private IPowerManager mMockIPowerManager;
+ @Mock Context mContext;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ // setup context mock
+ doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
+ // setup power manager mock
+ PowerManager mPowerManager;
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+ mPowerManager = new PowerManager(mContext, mMockIPowerManager, null);
+
+ Object mLock = new Object();
+ // setup a spy on attention manager
+ AttentionManagerService mAttentionManager = new AttentionManagerService(
+ mContext,
+ mPowerManager,
+ mLock,
+ mMockHandler);
+ mSpyAttentionManager = Mockito.spy(mAttentionManager);
+ // setup a spy on user state
+ ComponentName componentName = new ComponentName("a", "b");
+ mSpyAttentionManager.mComponentName = componentName;
+ UserState mUserState = new UserState(0,
+ mContext,
+ mLock,
+ componentName);
+ mUserState.mService = new MockIAttentionService();
+ mSpyUserState = spy(mUserState);
+ }
+
+ @Test
+ public void testCancelAttentionCheck_noCrashWhenNoUserStateLocked() {
+ mSpyAttentionManager.cancelAttentionCheck(null);
+ }
+
+ @Test
+ public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() {
+ mSpyUserState.mCurrentAttentionCheck =
+ new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback);
+ doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked();
+ mSpyAttentionManager.cancelAttentionCheck(null);
+ }
+
+ @Test
+ public void testCancelAttentionCheck_cancelCallbackWhenMatched() {
+ mSpyUserState.mCurrentAttentionCheck =
+ new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback);
+ doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked();
+ mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal);
+ verify(mSpyAttentionManager).cancel(any());
+ }
+
+ @Test
+ public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException {
+ doReturn(false).when(mMockIPowerManager).isInteractive();
+ AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
+ assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse();
+ }
+
+ @Test
+ public void testCheckAttention_callOnSuccess() throws RemoteException {
+ doReturn(true).when(mSpyAttentionManager).isServiceEnabled();
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+ doReturn(mSpyUserState).when(mSpyAttentionManager).getOrCreateCurrentUserStateLocked();
+ doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
+
+ AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
+ mSpyAttentionManager.checkAttention(mTimeout, callback);
+ verify(callback).onSuccess(anyInt(), anyLong());
+ }
+
+ private class MockIAttentionService implements IAttentionService {
+ public void checkAttention(IAttentionCallback callback) throws RemoteException {
+ callback.onSuccess(0, 0);
+ }
+ public void cancelAttentionCheck(IAttentionCallback callback) {
+ }
+ public IBinder asBinder() {
+ return null;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
deleted file mode 100644
index 97a6e66..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ /dev/null
@@ -1,1217 +0,0 @@
-/*
- * Copyright (C) 2019 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.backup.testutils;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ChangedPackages;
-import android.content.pm.IDexModuleRegisterCallback;
-import android.content.pm.IOnPermissionsChangeListener;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageDeleteObserver2;
-import android.content.pm.IPackageInstaller;
-import android.content.pm.IPackageManager;
-import android.content.pm.IPackageMoveObserver;
-import android.content.pm.IPackageStatsObserver;
-import android.content.pm.InstrumentationInfo;
-import android.content.pm.KeySet;
-import android.content.pm.ModuleInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.SuspendDialogInfo;
-import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.VersionedPackage;
-import android.content.pm.dex.IArtManager;
-import android.graphics.Bitmap;
-import android.os.IBinder;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * Stub for IPackageManager to use in tests.
- */
-public class IPackageManagerStub implements IPackageManager {
- public static PackageInfo sPackageInfo;
- public static int sApplicationEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-
- @Override
- public PackageInfo getPackageInfo(String packageName, int flags, int userId)
- throws RemoteException {
- return sPackageInfo;
- }
-
- @Override
- public int getApplicationEnabledSetting(String packageName, int userId) throws RemoteException {
- return sApplicationEnabledSetting;
- }
-
- @Override
- public void checkPackageStartable(String packageName, int userId) throws RemoteException {
-
- }
-
- @Override
- public boolean isPackageAvailable(String packageName, int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags,
- int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public int getPackageUid(String packageName, int flags, int userId) throws RemoteException {
- return 0;
- }
-
- @Override
- public int[] getPackageGids(String packageName, int flags, int userId) throws RemoteException {
- return new int[0];
- }
-
- @Override
- public String[] currentToCanonicalPackageNames(String[] names) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public String[] canonicalToCurrentPackageNames(String[] names) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public PermissionInfo getPermissionInfo(String name, String packageName, int flags)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice queryPermissionsByGroup(String group, int flags)
- throws RemoteException {
- return null;
- }
-
- @Override
- public PermissionGroupInfo getPermissionGroupInfo(String name, int flags)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getAllPermissionGroups(int flags) throws RemoteException {
- return null;
- }
-
- @Override
- public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ActivityInfo getActivityInfo(ComponentName className, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public boolean activitySupportsIntent(ComponentName className, Intent intent,
- String resolvedType)
- throws RemoteException {
- return false;
- }
-
- @Override
- public ActivityInfo getReceiverInfo(ComponentName className, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ProviderInfo getProviderInfo(ComponentName className, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public int checkPermission(String permName, String pkgName, int userId) throws RemoteException {
- return 0;
- }
-
- @Override
- public int checkUidPermission(String permName, int uid) throws RemoteException {
- return 0;
- }
-
- @Override
- public boolean addPermission(PermissionInfo info) throws RemoteException {
- return false;
- }
-
- @Override
- public void removePermission(String name) throws RemoteException {
-
- }
-
- @Override
- public void grantRuntimePermission(String packageName, String permissionName, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void revokeRuntimePermission(String packageName, String permissionName, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void resetRuntimePermissions() throws RemoteException {
-
- }
-
- @Override
- public int getPermissionFlags(String permissionName, String packageName, int userId)
- throws RemoteException {
- return 0;
- }
-
- @Override
- public void updatePermissionFlags(String permissionName, String packageName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public List<String> getWhitelistedRestrictedPermissions(String packageName, int flags,
- int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public boolean addWhitelistedRestrictedPermission(String packageName, String permission,
- int whitelistFlags, int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean removeWhitelistedRestrictedPermission(String packageName, String permission,
- int whitelistFlags, int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName,
- int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean isProtectedBroadcast(String actionName) throws RemoteException {
- return false;
- }
-
- @Override
- public int checkSignatures(String pkg1, String pkg2) throws RemoteException {
- return 0;
- }
-
- @Override
- public int checkUidSignatures(int uid1, int uid2) throws RemoteException {
- return 0;
- }
-
- @Override
- public List<String> getAllPackages() throws RemoteException {
- return null;
- }
-
- @Override
- public String[] getPackagesForUid(int uid) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public String getNameForUid(int uid) throws RemoteException {
- return null;
- }
-
- @Override
- public String[] getNamesForUids(int[] uids) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public int getUidForSharedUser(String sharedUserName) throws RemoteException {
- return 0;
- }
-
- @Override
- public int getFlagsForUid(int uid) throws RemoteException {
- return 0;
- }
-
- @Override
- public int getPrivateFlagsForUid(int uid) throws RemoteException {
- return 0;
- }
-
- @Override
- public boolean isUidPrivileged(int uid) throws RemoteException {
- return false;
- }
-
- @Override
- public String[] getAppOpPermissionPackages(String permissionName) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
- int targetUserId) throws RemoteException {
- return false;
- }
-
- @Override
- public ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags,
- int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
- String[] specificTypes, Intent intent, String resolvedType, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice queryIntentReceivers(Intent intent, String resolvedType, int flags,
- int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags,
- int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType,
- int flags, int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getInstalledPackages(int flags, int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getPackagesHoldingPermissions(String[] permissions, int flags,
- int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getInstalledApplications(int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getPersistentApplications(int flags) throws RemoteException {
- return null;
- }
-
- @Override
- public ProviderInfo resolveContentProvider(String name, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo)
- throws RemoteException {
-
- }
-
- @Override
- public ParceledListSlice queryContentProviders(String processName, int uid, int flags,
- String metaDataKey) throws RemoteException {
- return null;
- }
-
- @Override
- public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice queryInstrumentation(String targetPackage, int flags)
- throws RemoteException {
- return null;
- }
-
- @Override
- public void finishPackageInstall(int token, boolean didLaunch) throws RemoteException {
-
- }
-
- @Override
- public void setInstallerPackageName(String targetPackage, String installerPackageName)
- throws RemoteException {
-
- }
-
- @Override
- public void setApplicationCategoryHint(String packageName, int categoryHint,
- String callerPackageName) throws RemoteException {
-
- }
-
- @Override
- public void deletePackageAsUser(String packageName, int versionCode,
- IPackageDeleteObserver observer, int userId, int flags) throws RemoteException {
-
- }
-
- @Override
- public void deletePackageVersioned(VersionedPackage versionedPackage,
- IPackageDeleteObserver2 observer, int userId, int flags) throws RemoteException {
-
- }
-
- @Override
- public String getInstallerPackageName(String packageName) throws RemoteException {
- return null;
- }
-
- @Override
- public void resetApplicationPreferences(int userId) throws RemoteException {
-
- }
-
- @Override
- public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags)
- throws RemoteException {
- return null;
- }
-
- @Override
- public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
- IntentFilter filter, int match, ComponentName activity) throws RemoteException {
-
- }
-
- @Override
- public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set,
- ComponentName activity, int userId) throws RemoteException {
-
- }
-
- @Override
- public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set,
- ComponentName activity, int userId) throws RemoteException {
-
- }
-
- @Override
- public void clearPackagePreferredActivities(String packageName) throws RemoteException {
-
- }
-
- @Override
- public int getPreferredActivities(List<IntentFilter> outFilters,
- List<ComponentName> outActivities, String packageName) throws RemoteException {
- return 0;
- }
-
- @Override
- public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
- int userId) throws RemoteException {
-
- }
-
- @Override
- public void clearPackagePersistentPreferredActivities(String packageName, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
- int sourceUserId, int targetUserId, int flags) throws RemoteException {
-
- }
-
- @Override
- public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage)
- throws RemoteException {
-
- }
-
- @Override
- public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
- int restrictionFlags, int userId) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
- PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo,
- String callingPackage, int userId) throws RemoteException {
- return new String[0];
- }
-
- @Override
- public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId)
- throws RemoteException {
- return new String[0];
- }
-
- @Override
- public boolean isPackageSuspendedForUser(String packageName, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public byte[] getPreferredActivityBackup(int userId) throws RemoteException {
- return new byte[0];
- }
-
- @Override
- public void restorePreferredActivities(byte[] backup, int userId) throws RemoteException {
-
- }
-
- @Override
- public byte[] getDefaultAppsBackup(int userId) throws RemoteException {
- return new byte[0];
- }
-
- @Override
- public void restoreDefaultApps(byte[] backup, int userId) throws RemoteException {
-
- }
-
- @Override
- public byte[] getIntentFilterVerificationBackup(int userId) throws RemoteException {
- return new byte[0];
- }
-
- @Override
- public void restoreIntentFilterVerification(byte[] backup, int userId) throws RemoteException {
-
- }
-
- @Override
- public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates)
- throws RemoteException {
- return null;
- }
-
- @Override
- public void setHomeActivity(ComponentName className, int userId) throws RemoteException {
-
- }
-
- @Override
- public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags,
- int userId) throws RemoteException {
-
- }
-
- @Override
- public int getComponentEnabledSetting(ComponentName componentName, int userId)
- throws RemoteException {
- return 0;
- }
-
- @Override
- public void setApplicationEnabledSetting(String packageName, int newState, int flags,
- int userId,
- String callingPackage) throws RemoteException {
-
- }
-
- @Override
- public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
- String apkFile,
- int pid) throws RemoteException {
-
- }
-
- @Override
- public void flushPackageRestrictionsAsUser(int userId) throws RemoteException {
-
- }
-
- @Override
- public void setPackageStoppedState(String packageName, boolean stopped, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void freeStorageAndNotify(String volumeUuid, long freeStorageSize, int storageFlags,
- IPackageDataObserver observer) throws RemoteException {
-
- }
-
- @Override
- public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags,
- IntentSender pi) throws RemoteException {
-
- }
-
- @Override
- public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer)
- throws RemoteException {
-
- }
-
- @Override
- public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
- IPackageDataObserver observer) throws RemoteException {
-
- }
-
- @Override
- public void clearApplicationUserData(String packageName, IPackageDataObserver observer,
- int userId) throws RemoteException {
-
- }
-
- @Override
- public void clearApplicationProfileData(String packageName) throws RemoteException {
-
- }
-
- @Override
- public void getPackageSizeInfo(String packageName, int userHandle,
- IPackageStatsObserver observer)
- throws RemoteException {
-
- }
-
- @Override
- public String[] getSystemSharedLibraryNames() throws RemoteException {
- return new String[0];
- }
-
- @Override
- public ParceledListSlice getSystemAvailableFeatures() throws RemoteException {
- return null;
- }
-
- @Override
- public boolean hasSystemFeature(String name, int version) throws RemoteException {
- return false;
- }
-
- @Override
- public void enterSafeMode() throws RemoteException {
-
- }
-
- @Override
- public boolean isSafeMode() throws RemoteException {
- return false;
- }
-
- @Override
- public void systemReady() throws RemoteException {
-
- }
-
- @Override
- public boolean hasSystemUidErrors() throws RemoteException {
- return false;
- }
-
- @Override
- public void performFstrimIfNeeded() throws RemoteException {
-
- }
-
- @Override
- public void updatePackagesIfNeeded() throws RemoteException {
-
- }
-
- @Override
- public void notifyPackageUse(String packageName, int reason) throws RemoteException {
-
- }
-
- @Override
- public void notifyDexLoad(String loadingPackageName, List<String> classLoadersNames,
- List<String> classPaths, String loaderIsa) throws RemoteException {
-
- }
-
- @Override
- public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
- IDexModuleRegisterCallback callback) throws RemoteException {
-
- }
-
- @Override
- public boolean performDexOptMode(String packageName, boolean checkProfiles,
- String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)
- throws RemoteException {
- return false;
- }
-
- @Override
- public boolean performDexOptSecondary(String packageName, String targetCompilerFilter,
- boolean force) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean compileLayouts(String packageName) throws RemoteException {
- return false;
- }
-
- @Override
- public void dumpProfiles(String packageName) throws RemoteException {
-
- }
-
- @Override
- public void forceDexOpt(String packageName) throws RemoteException {
-
- }
-
- @Override
- public boolean runBackgroundDexoptJob(List<String> packageNames) throws RemoteException {
- return false;
- }
-
- @Override
- public void reconcileSecondaryDexFiles(String packageName) throws RemoteException {
-
- }
-
- @Override
- public int getMoveStatus(int moveId) throws RemoteException {
- return 0;
- }
-
- @Override
- public void registerMoveCallback(IPackageMoveObserver callback) throws RemoteException {
-
- }
-
- @Override
- public void unregisterMoveCallback(IPackageMoveObserver callback) throws RemoteException {
-
- }
-
- @Override
- public int movePackage(String packageName, String volumeUuid) throws RemoteException {
- return 0;
- }
-
- @Override
- public int movePrimaryStorage(String volumeUuid) throws RemoteException {
- return 0;
- }
-
- @Override
- public boolean addPermissionAsync(PermissionInfo info) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean setInstallLocation(int loc) throws RemoteException {
- return false;
- }
-
- @Override
- public int getInstallLocation() throws RemoteException {
- return 0;
- }
-
- @Override
- public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
- int installReason) throws RemoteException {
- return 0;
- }
-
- @Override
- public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
-
- }
-
- @Override
- public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
- long millisecondsToDelay) throws RemoteException {
-
- }
-
- @Override
- public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains)
- throws RemoteException {
-
- }
-
- @Override
- public int getIntentVerificationStatus(String packageName, int userId) throws RemoteException {
- return 0;
- }
-
- @Override
- public boolean updateIntentVerificationStatus(String packageName, int status, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public ParceledListSlice getIntentFilterVerifications(String packageName)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getAllIntentFilters(String packageName) throws RemoteException {
- return null;
- }
-
- @Override
- public boolean setDefaultBrowserPackageName(String packageName, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public String getDefaultBrowserPackageName(int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
- return null;
- }
-
- @Override
- public boolean isFirstBoot() throws RemoteException {
- return false;
- }
-
- @Override
- public boolean isOnlyCoreApps() throws RemoteException {
- return false;
- }
-
- @Override
- public boolean isDeviceUpgrading() throws RemoteException {
- return false;
- }
-
- @Override
- public void setPermissionEnforced(String permission, boolean enforced) throws RemoteException {
-
- }
-
- @Override
- public boolean isPermissionEnforced(String permission) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean isStorageLow() throws RemoteException {
- return false;
- }
-
- @Override
- public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public boolean getApplicationHiddenSettingAsUser(String packageName, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden)
- throws RemoteException {
-
- }
-
- @Override
- public boolean setSystemAppInstallState(String packageName, boolean installed, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public IPackageInstaller getPackageInstaller() throws RemoteException {
- return null;
- }
-
- @Override
- public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public boolean getBlockUninstallForUser(String packageName, int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public KeySet getKeySetByAlias(String packageName, String alias) throws RemoteException {
- return null;
- }
-
- @Override
- public KeySet getSigningKeySet(String packageName) throws RemoteException {
- return null;
- }
-
- @Override
- public boolean isPackageSignedByKeySet(String packageName, KeySet ks) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks)
- throws RemoteException {
- return false;
- }
-
- @Override
- public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener)
- throws RemoteException {
-
- }
-
- @Override
- public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener)
- throws RemoteException {
-
- }
-
- @Override
- public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames,
- int userId) throws RemoteException {
-
- }
-
- @Override
- public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames,
- int userId) throws RemoteException {
-
- }
-
- @Override
- public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public String getPermissionControllerPackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getInstantApps(int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public byte[] getInstantAppCookie(String packageName, int userId) throws RemoteException {
- return new byte[0];
- }
-
- @Override
- public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public Bitmap getInstantAppIcon(String packageName, int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public boolean isInstantApp(String packageName, int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp)
- throws RemoteException {
- return false;
- }
-
- @Override
- public void setUpdateAvailable(String packageName, boolean updateAvaialble)
- throws RemoteException {
-
- }
-
- @Override
- public String getServicesSystemSharedLibraryPackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public String getSharedSystemSharedLibraryPackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public ChangedPackages getChangedPackages(int sequenceNumber, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public boolean isPackageDeviceAdminOnAnyUser(String packageName) throws RemoteException {
- return false;
- }
-
- @Override
- public int getInstallReason(String packageName, int userId) throws RemoteException {
- return 0;
- }
-
- @Override
- public ParceledListSlice getSharedLibraries(String packageName, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public ParceledListSlice getDeclaredSharedLibraries(String packageName, int flags, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public boolean canRequestPackageInstalls(String packageName, int userId)
- throws RemoteException {
- return false;
- }
-
- @Override
- public void deletePreloadsFileCache() throws RemoteException {
-
- }
-
- @Override
- public ComponentName getInstantAppResolverComponent() throws RemoteException {
- return null;
- }
-
- @Override
- public ComponentName getInstantAppResolverSettingsComponent() throws RemoteException {
- return null;
- }
-
- @Override
- public ComponentName getInstantAppInstallerComponent() throws RemoteException {
- return null;
- }
-
- @Override
- public String getInstantAppAndroidId(String packageName, int userId) throws RemoteException {
- return null;
- }
-
- @Override
- public IArtManager getArtManager() throws RemoteException {
- return null;
- }
-
- @Override
- public void setHarmfulAppWarning(String packageName, CharSequence warning, int userId)
- throws RemoteException {
-
- }
-
- @Override
- public CharSequence getHarmfulAppWarning(String packageName, int userId)
- throws RemoteException {
- return null;
- }
-
- @Override
- public boolean hasSigningCertificate(String packageName, byte[] signingCertificate, int flags)
- throws RemoteException {
- return false;
- }
-
- @Override
- public boolean hasUidSigningCertificate(int uid, byte[] signingCertificate, int flags)
- throws RemoteException {
- return false;
- }
-
- @Override
- public String getSystemTextClassifierPackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public String getWellbeingPackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public String getSystemCaptionsServicePackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public String getAttentionServicePackageName() throws RemoteException {
- return null;
- }
-
- public String getIncidentReportApproverPackageName() throws RemoteException {
- return null;
- }
-
- @Override
- public String getAppPredictionServicePackageName() {
- return null;
- }
-
- @Override
- public boolean isPackageStateProtected(String packageName, int userId) throws RemoteException {
- return false;
- }
-
- @Override
- public void sendDeviceCustomizationReadyBroadcast() throws RemoteException {
-
- }
-
- @Override
- public List<ModuleInfo> getInstalledModules(int flags) throws RemoteException {
- return null;
- }
-
- @Override
- public ModuleInfo getModuleInfo(String packageName, int flags) throws RemoteException {
- return null;
- }
-
- @Override
- public int getRuntimePermissionsVersion(int userId) throws RemoteException {
- return 0;
- }
-
- @Override
- public void setRuntimePermissionsVersion(int version, int userId) throws RemoteException {
-
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index a92b576..a901175 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -36,7 +37,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.backup.testutils.IPackageManagerStub;
import org.junit.Before;
import org.junit.Test;
@@ -54,14 +54,12 @@
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
- private IPackageManagerStub mPackageManagerStub;
private PackageManagerInternal mMockPackageManagerInternal;
private int mUserId;
@Before
public void setUp() throws Exception {
- mPackageManagerStub = new IPackageManagerStub();
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mUserId = UserHandle.USER_SYSTEM;
@@ -76,7 +74,7 @@
applicationInfo.packageName = TEST_PACKAGE_NAME;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isFalse();
}
@@ -91,7 +89,7 @@
applicationInfo.packageName = TEST_PACKAGE_NAME;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isFalse();
}
@@ -105,7 +103,7 @@
applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isFalse();
}
@@ -118,12 +116,11 @@
applicationInfo.uid = Process.SYSTEM_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
-
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isTrue();
}
@@ -136,12 +133,11 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
-
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isTrue();
}
@@ -154,12 +150,11 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
-
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isTrue();
}
@@ -172,12 +167,11 @@
applicationInfo.uid = Process.SYSTEM_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
-
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isFalse();
}
@@ -190,12 +184,11 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
-
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isFalse();
}
@@ -208,12 +201,11 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
-
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub, mUserId);
+ mMockPackageManagerInternal, mUserId);
assertThat(isEligible).isFalse();
}
@@ -226,12 +218,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.enabled = true;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
- mUserId);
+ boolean isDisabled =
+ AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
assertThat(isDisabled).isFalse();
}
@@ -244,12 +235,12 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.enabled = false;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
- mUserId);
+ boolean isDisabled =
+ AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
assertThat(isDisabled).isTrue();
}
@@ -261,12 +252,12 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
- mUserId);
+ boolean isDisabled =
+ AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
assertThat(isDisabled).isFalse();
}
@@ -278,12 +269,12 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
- mUserId);
+ boolean isDisabled =
+ AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
assertThat(isDisabled).isTrue();
}
@@ -295,12 +286,11 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
-
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
- mUserId);
+ boolean isDisabled =
+ AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
assertThat(isDisabled).isTrue();
}
@@ -312,12 +302,11 @@
applicationInfo.uid = Process.FIRST_APPLICATION_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
+ when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
- IPackageManagerStub.sApplicationEnabledSetting =
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
-
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
- mUserId);
+ boolean isDisabled =
+ AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId);
assertThat(isDisabled).isTrue();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 355ff63..2d101dd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -349,6 +349,7 @@
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
+ mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 91d3e5e..7e3d4b4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -206,6 +206,7 @@
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
+ mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
mUsers = new ArrayList<>();
mUsers.add(new UserInfo(0, "system", 0));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 08d8333..8936450 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -110,6 +110,7 @@
private ZenModeHelper mZenModeHelperSpy;
private Context mContext;
private ContentResolver mContentResolver;
+ @Mock AppOpsManager mAppOps;
@Before
public void setUp() {
@@ -127,6 +128,7 @@
e.toString());
}
+ when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps);
when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
mConditionProviders = new ConditionProviders(mContext, new UserProfiles(),
AppGlobals.getPackageManager());
@@ -219,10 +221,10 @@
Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyInt());
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt());
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ALARM);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_MEDIA);
}
@@ -233,9 +235,9 @@
Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
AudioAttributes.USAGE_ALARM);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
AudioAttributes.USAGE_MEDIA);
}
@@ -244,13 +246,13 @@
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_ALARM);
// Media is a catch-all that includes games
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_MEDIA);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_GAME);
}
@@ -262,17 +264,17 @@
mZenModeHelperSpy.applyRestrictions();
// Total silence will silence alarms, media and system noises (but not vibrations)
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_ALARM);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_MEDIA);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_GAME);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_VIBRATE);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_UNKNOWN);
}
@@ -283,19 +285,19 @@
mZenModeHelperSpy.applyRestrictions();
// Alarms only mode will not silence alarms
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ALARM);
// Alarms only mode will not silence media
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_MEDIA);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_GAME);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_UNKNOWN);
// Alarms only will silence system noises (but not vibrations)
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
}
@@ -306,9 +308,9 @@
mZenModeHelperSpy.applyRestrictions();
// Alarms only mode will silence calls despite priority-mode config
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
}
@@ -319,7 +321,7 @@
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ALARM);
}
@@ -334,19 +336,64 @@
for (int usage : AudioAttributes.SDK_USAGES) {
if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) {
// only mute audio, not vibrations
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, usage,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, usage,
AppOpsManager.OP_PLAY_AUDIO);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, usage,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, usage,
AppOpsManager.OP_VIBRATE);
} else {
boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
!= AudioAttributes.SUPPRESSIBLE_NEVER;
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, shouldMute, usage);
}
}
}
@Test
+ public void testApplyRestrictions_whitelist_priorityOnlyMode() {
+ mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(new String[]{PKG_O}));
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(new String[]{PKG_O}));
+ }
+ }
+
+ @Test
+ public void testApplyRestrictions_whitelist_alarmsOnlyMode() {
+ mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_ALARMS;
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null));
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null));
+ }
+ }
+
+ @Test
+ public void testApplyRestrictions_whitelist_totalSilenceMode() {
+ mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_NO_INTERRUPTIONS;
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null));
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null));
+ }
+ }
+
+ @Test
public void testZenUpgradeNotification() {
// shows zen upgrade notification if stored settings says to shows,
// zen has not been updated, boot is completed
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 263f650..f51ce13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -44,14 +45,18 @@
private static final int FAKE_CALLING_UID = 667;
@Test
- public void testIsAnyNonToastWindowVisibleForUid_oneToastOneNonToastBothVisible() {
+ public void testIsAnyNonToastWindowVisibleForUid_oneToastOneAppStartOneNonToastBothVisible() {
final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+ final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting",
+ FAKE_CALLING_UID);
toastyToast.mHasSurface = true;
app.mHasSurface = true;
+ appStart.mHasSurface = true;
assertTrue(toastyToast.isVisibleNow());
assertTrue(app.isVisibleNow());
+ assertTrue(appStart.isVisibleNow());
assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
}
@@ -65,6 +70,16 @@
}
@Test
+ public void testIsAnyNonToastWindowVisibleForUid_onlyAppStartingVisible() {
+ final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting",
+ FAKE_CALLING_UID);
+ appStart.mHasSurface = true;
+
+ assertTrue(appStart.isVisibleNow());
+ assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+ }
+
+ @Test
public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() {
final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar", FAKE_CALLING_UID);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 79e6851..5b7d816 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1095,6 +1095,14 @@
Slog.d(TAG, "Clear notification");
mUsbNotificationId = 0;
}
+ // Not relevant for automotive.
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)
+ && id == SystemMessage.NOTE_USB_CHARGING) {
+ mUsbNotificationId = 0;
+ return;
+ }
+
if (id != 0) {
CharSequence title = r.getText(titleRes);
PendingIntent pi;
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 6382acf..6000b56 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -68,6 +68,7 @@
public void onExtrasRemoved(Conference c, List<String> keys) {}
public void onConferenceStateChanged(Conference c, boolean isConference) {}
public void onAddressChanged(Conference c, Uri newAddress, int presentation) {}
+ public void onConnectionEvent(Conference c, String event, Bundle extras) {}
public void onCallerDisplayNameChanged(
Conference c, String callerDisplayName, int presentation) {}
}
@@ -1024,4 +1025,14 @@
}
onExtrasChanged(b);
}
+
+ /**
+ * See {@link Connection#sendConnectionEvent(String, Bundle)}
+ * @hide
+ */
+ public void sendConnectionEvent(String event, Bundle extras) {
+ for (Listener l : mListeners) {
+ l.onConnectionEvent(this, event, extras);
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 9bafbe0..49b34b3 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1279,6 +1279,14 @@
mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
}
}
+
+ @Override
+ public void onConnectionEvent(Conference c, String event, Bundle extras) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.onConnectionEvent(id, event, extras);
+ }
+ }
};
private final Connection.Listener mConnectionListener = new Connection.Listener() {
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 037e475..9bba2e8 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3039,6 +3039,7 @@
* The {@code content://} style URL for this table. Can be appended with a part ID to
* address individual parts.
*/
+ @NonNull
public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, "part");
/**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 3087516..258a873 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -243,4 +243,23 @@
protected void log(String s) {
Rlog.w(mTag, s);
}
+
+ /** @hide */
+ protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
+ if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
+ return value;
+ }
+
+ /** @hide */
+ protected static final long inRangeOrUnavailable(long value, long rangeMin, long rangeMax) {
+ if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE_LONG;
+ return value;
+ }
+
+ /** @hide */
+ protected static final int inRangeOrUnavailable(
+ int value, int rangeMin, int rangeMax, int special) {
+ if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
+ return value;
+ }
}
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 4c00611..880d3db 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -28,12 +28,25 @@
private static final String TAG = CellIdentityCdma.class.getSimpleName();
private static final boolean DBG = false;
+ private static final int NETWORK_ID_MAX = 65535;
+ private static final int SYSTEM_ID_MAX = 32767;
+ private static final int BASESTATION_ID_MAX = 65535;
+
+ private static final int LONGITUDE_MIN = -2592000;
+ private static final int LONGITUDE_MAX = 2592000;
+
+ private static final int LATITUDE_MIN = -1296000;
+ private static final int LATITUDE_MAX = 1296000;
+
// Network Id 0..65535
private final int mNetworkId;
+
// CDMA System Id 0..32767
private final int mSystemId;
+
// Base Station Id 0..65535
private final int mBasestationId;
+
/**
* Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
* It is represented in units of 0.25 seconds and ranges from -2592000
@@ -41,6 +54,7 @@
* to +180 degrees).
*/
private final int mLongitude;
+
/**
* Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
* It is represented in units of 0.25 seconds and ranges from -1296000
@@ -78,9 +92,12 @@
public CellIdentityCdma(
int nid, int sid, int bid, int lon, int lat, String alphal, String alphas) {
super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas);
- mNetworkId = nid;
- mSystemId = sid;
- mBasestationId = bid;
+ mNetworkId = inRangeOrUnavailable(nid, 0, NETWORK_ID_MAX);
+ mSystemId = inRangeOrUnavailable(sid, 0, SYSTEM_ID_MAX);
+ mBasestationId = inRangeOrUnavailable(bid, 0, BASESTATION_ID_MAX);
+ lat = inRangeOrUnavailable(lat, LATITUDE_MIN, LATITUDE_MAX);
+ lon = inRangeOrUnavailable(lon, LONGITUDE_MIN, LONGITUDE_MAX);
+
if (!isNullIsland(lat, lon)) {
mLongitude = lon;
mLatitude = lat;
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 864540d..25c6577 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -31,6 +31,11 @@
private static final String TAG = CellIdentityGsm.class.getSimpleName();
private static final boolean DBG = false;
+ private static final int MAX_LAC = 65535;
+ private static final int MAX_CID = 65535;
+ private static final int MAX_ARFCN = 65535;
+ private static final int MAX_BSIC = 63;
+
// 16-bit Location Area Code, 0..65535
private final int mLac;
// 16-bit GSM Cell Identity described in TS 27.007, 0..65535
@@ -68,10 +73,10 @@
public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
String mncStr, String alphal, String alphas) {
super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
- mLac = lac;
- mCid = cid;
- mArfcn = arfcn;
- mBsic = bsic;
+ mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
+ mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
+ mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
+ mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
}
/** @hide */
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 14503c7..997b19f 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -32,6 +32,12 @@
private static final String TAG = CellIdentityLte.class.getSimpleName();
private static final boolean DBG = false;
+ private static final int MAX_CI = 268435455;
+ private static final int MAX_PCI = 503;
+ private static final int MAX_TAC = 65535;
+ private static final int MAX_EARFCN = 262143;
+ private static final int MAX_BANDWIDTH = 20000;
+
// 28-bit cell identity
private final int mCi;
// physical cell id 0..503
@@ -89,11 +95,11 @@
public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
String mncStr, String alphal, String alphas) {
super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
- mCi = ci;
- mPci = pci;
- mTac = tac;
- mEarfcn = earfcn;
- mBandwidth = bandwidth;
+ mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
+ mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
+ mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
+ mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
+ mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
}
/** @hide */
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 62d23ce..6df60ba 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -29,6 +29,11 @@
public final class CellIdentityNr extends CellIdentity {
private static final String TAG = "CellIdentityNr";
+ private static final int MAX_PCI = 1007;
+ private static final int MAX_TAC = 65535;
+ private static final int MAX_NRARFCN = 3279165;
+ private static final long MAX_NCI = 68719476735L;
+
private final int mNrArfcn;
private final int mPci;
private final int mTac;
@@ -41,6 +46,7 @@
* @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
* @param mccStr 3-digit Mobile Country Code in string format.
* @param mncStr 2 or 3-digit Mobile Network Code in string format.
+ * @param nci The 36-bit NR Cell Identity in range [0, 68719476735].
* @param alphal long alpha Operator Name String or Enhanced Operator Name String.
* @param alphas short alpha Operator Name String or Enhanced Operator Name String.
*
@@ -49,10 +55,10 @@
public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr,
long nci, String alphal, String alphas) {
super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
- mPci = pci;
- mTac = tac;
- mNrArfcn = nrArfcn;
- mNci = nci;
+ mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
+ mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
+ mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
+ mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
}
/** @hide */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 937de70..558e346 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -30,12 +30,18 @@
private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
private static final boolean DBG = false;
+ private static final int MAX_LAC = 65535;
+ private static final int MAX_CID = 268435455;
+ private static final int MAX_CPID = 127;
+ private static final int MAX_UARFCN = 65535;
+
// 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown.
private final int mLac;
// 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.UNAVAILABLE
// if unknown.
private final int mCid;
- // 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE if unknown.
+ // 8-bit Cell Parameters ID described in TS 25.331 sec 10.3.6.9,
+ // 0..127, CellInfo.UNAVAILABLE if unknown.
private final int mCpid;
// 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
private final int mUarfcn;
@@ -68,10 +74,10 @@
public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn,
String alphal, String alphas) {
super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
- mLac = lac;
- mCid = cid;
- mCpid = cpid;
- mUarfcn = uarfcn;
+ mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
+ mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
+ mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
+ mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
}
private CellIdentityTdscdma(CellIdentityTdscdma cid) {
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index b4a2ead..031fed1 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -31,6 +31,11 @@
private static final String TAG = CellIdentityWcdma.class.getSimpleName();
private static final boolean DBG = false;
+ private static final int MAX_LAC = 65535;
+ private static final int MAX_CID = 268435455;
+ private static final int MAX_PSC = 511;
+ private static final int MAX_UARFCN = 16383; // a 14 bit number; TS 25.331 ex sec 10.3.8.15
+
// 16-bit Location Area Code, 0..65535
private final int mLac;
// 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455
@@ -68,10 +73,10 @@
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
String mccStr, String mncStr, String alphal, String alphas) {
super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
- mLac = lac;
- mCid = cid;
- mPsc = psc;
- mUarfcn = uarfcn;
+ mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
+ mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
+ mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
+ mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
}
/** @hide */
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0808adf..d4f9874 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -294,6 +294,19 @@
public static final String SUBSCRIPTION_TYPE = "subscription_type";
/**
+ * TelephonyProvider column name white_listed_apn_data.
+ * It's a bitmask of APN types that will be allowed on this subscription even if it's metered
+ * and mobile data is turned off by the user.
+ * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS
+ * data connection to setup even if MMS is metered and mobile_data is turned off on that
+ * subscription.
+ *
+ * Default value is 0.
+ */
+ /** @hide */
+ public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data";
+
+ /**
* This constant is to designate a subscription as a Local-SIM Subscription.
* <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the
* device.
@@ -3086,6 +3099,31 @@
return subId;
}
+ /**
+ * Set whether a subscription always allows MMS connection. If true, MMS network
+ * request will be accepted by telephony even if user turns "mobile data" off
+ * on this subscription.
+ *
+ * @param subId which subscription it's setting to.
+ * @param alwaysAllow whether Mms data is always allowed.
+ * @return whether operation is successful.
+ *
+ * @hide
+ */
+ public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.setAlwaysAllowMmsData(subId, alwaysAllow);
+ }
+ } catch (RemoteException ex) {
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2bd4f8f..18b9fbb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10948,4 +10948,33 @@
}
return new Pair<Integer, Integer>(-1, -1);
}
+
+
+ /**
+ * Return whether MMS data is enabled. This will tell if framework will accept a MMS network
+ * request on a subId.
+ *
+ * Mms is enabled if:
+ * 1) user data is turned on, or
+ * 2) MMS is un-metered for this subscription, or
+ * 3) alwaysAllowMms setting {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+ *
+ * @return whether MMS data is allowed.
+ *
+ * @hide
+ */
+ public boolean isMmsDataEnabled() {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.isMmsDataEnabled(getSubId(), pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index e9aede7..be58723 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -781,8 +781,13 @@
}
/**
- * Change the user's setting for RTT capability of this device.
- * @param isEnabled if true RTT will be enabled during calls.
+ * Sets the capability of RTT for IMS calls placed on this subscription.
+ *
+ * Note: This does not affect the value of
+ * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
+ * for RTT. That value is enabled/disabled separately by the user through the Accessibility
+ * settings.
+ * @param isEnabled if true RTT should be enabled during calls made on this subscription.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRttCapabilitySetting(boolean isEnabled) {
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index c008711..e6777c17 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -40,8 +40,6 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
-import static android.Manifest.permission.MODIFY_PHONE_STATE;
-
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
* ImsService must register the service in their AndroidManifest to be detected by the framework.
@@ -229,8 +227,8 @@
private void setupFeature(ImsFeature f, int slotId, int featureType,
IImsFeatureStatusCallback c) {
- f.addImsFeatureStatusCallback(c);
f.initialize(this, slotId);
+ f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
}
diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
index e8fcac1..de4f174 100644
--- a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.content.Intent;
import android.os.IInterface;
import android.os.RemoteException;
import android.telephony.SubscriptionManager;
@@ -42,32 +41,6 @@
private static final String LOG_TAG = "ImsFeature";
- /**
- * Action to broadcast when ImsService is up.
- * Internal use only.
- * Only defined here separately compatibility purposes with the old ImsService.
- * @hide
- */
- public static final String ACTION_IMS_SERVICE_UP =
- "com.android.ims.IMS_SERVICE_UP";
-
- /**
- * Action to broadcast when ImsService is down.
- * Internal use only.
- * Only defined here separately for compatibility purposes with the old ImsService.
- * @hide
- */
- public static final String ACTION_IMS_SERVICE_DOWN =
- "com.android.ims.IMS_SERVICE_DOWN";
-
- /**
- * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
- * A long value; the phone ID corresponding to the IMS service coming up or down.
- * Only defined here separately for compatibility purposes with the old ImsService.
- * @hide
- */
- public static final String EXTRA_PHONE_ID = "android:phone_id";
-
// Invalid feature value
public static final int INVALID = -1;
// ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
@@ -162,30 +135,6 @@
}
}
}
- sendImsServiceIntent(state);
- }
-
- /**
- * Provide backwards compatibility using deprecated service UP/DOWN intents.
- */
- private void sendImsServiceIntent(@ImsState int state) {
- if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- return;
- }
- Intent intent;
- switch (state) {
- case ImsFeature.STATE_NOT_AVAILABLE:
- case ImsFeature.STATE_INITIALIZING:
- intent = new Intent(ACTION_IMS_SERVICE_DOWN);
- break;
- case ImsFeature.STATE_READY:
- intent = new Intent(ACTION_IMS_SERVICE_UP);
- break;
- default:
- intent = new Intent(ACTION_IMS_SERVICE_DOWN);
- }
- intent.putExtra(EXTRA_PHONE_ID, mSlotId);
- mContext.sendBroadcast(intent);
}
/**
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 5e3f398..74af6bf 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
-import android.content.Intent;
import android.os.IInterface;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -395,30 +394,6 @@
}
}
}
- sendImsServiceIntent(state);
- }
-
- /**
- * Provide backwards compatibility using deprecated service UP/DOWN intents.
- */
- private void sendImsServiceIntent(@ImsState int state) {
- if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- return;
- }
- Intent intent;
- switch (state) {
- case ImsFeature.STATE_UNAVAILABLE:
- case ImsFeature.STATE_INITIALIZING:
- intent = new Intent(ACTION_IMS_SERVICE_DOWN);
- break;
- case ImsFeature.STATE_READY:
- intent = new Intent(ACTION_IMS_SERVICE_UP);
- break;
- default:
- intent = new Intent(ACTION_IMS_SERVICE_DOWN);
- }
- intent.putExtra(EXTRA_PHONE_ID, mSlotId);
- mContext.sendBroadcast(intent);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 6e8d038..bb5c251 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -94,6 +94,7 @@
public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
+ public static final int EVENT_APN_WHITE_LIST_CHANGE = BASE + 51;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index cfba052..b081228 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -279,4 +279,6 @@
int getSimStateForSlotIndex(int slotIndex);
boolean isActiveSubId(int subId, String callingPackage);
+
+ boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ecbbd6a..536c514 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1966,4 +1966,6 @@
int getRadioHalVersion();
boolean isModemEnabledForSlot(int slotIndex, String callingPackage);
+
+ boolean isMmsDataEnabled(int subId, String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 80fb58d..12b20ef 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -344,10 +344,22 @@
return true;
}
// if the calling package is null then return now as there's no way to perform the
- // DevicePolicyManager device / profile owner checks.
+ // DevicePolicyManager device / profile owner and AppOp checks
if (callingPackage == null) {
return false;
}
+ // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op.
+ long token = Binder.clearCallingIdentity();
+ AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(
+ Context.APP_OPS_SERVICE);
+ try {
+ if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
+ callingPackage) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// Allow access to a device / profile owner app.
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp
index 982fcba..5d8ed2c 100644
--- a/tests/FlickerTests/lib/Android.bp
+++ b/tests/FlickerTests/lib/Android.bp
@@ -20,7 +20,7 @@
srcs: ["src/**/*.java"],
static_libs: [
"androidx.test.janktesthelper",
- "cts-amwm-util",
+ "cts-wm-util",
"platformprotosnano",
"layersprotosnano",
"truth-prebuilt",
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index e0d74e0..c9e3404 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -34,6 +34,8 @@
public final class FrameworksTestsFilter extends SelectTest {
private static final String[] SELECTED_TESTS = {
+ // Test specifications for FrameworksMockingCoreTests.
+ "android.app.activity.ActivityThreadClientTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
"android.view.DisplayCutoutTest",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 9832485..3da22b4 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -129,12 +129,20 @@
constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
if (attr->type_mask & kMask) {
for (const auto& symbol : attr->symbols) {
- printer_->Print(symbol.symbol.name.value().entry);
- if (symbol.symbol.id) {
- printer_->Print("(");
+ if (symbol.symbol.name) {
+ printer_->Print(symbol.symbol.name.value().entry);
+
+ if (symbol.symbol.id) {
+ printer_->Print("(");
+ printer_->Print(symbol.symbol.id.value().to_string());
+ printer_->Print(")");
+ }
+ } else if (symbol.symbol.id) {
printer_->Print(symbol.symbol.id.value().to_string());
- printer_->Print(")");
+ } else {
+ printer_->Print("???");
}
+
printer_->Println(StringPrintf("=0x%08x", symbol.value));
}
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 950c6f8..9d4b837 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -967,6 +967,10 @@
* from the top-level domain and all the labels in domain_suffix_match shall be included in the
* certificate. The certificate may include additional sub-level labels in addition to the
* required labels.
+ * <p>More than one match string can be provided by using semicolons to separate the strings
+ * (e.g., example.org;example.com). When multiple strings are specified, a match with any one of
+ * the values is considered a sufficient match for the certificate, i.e., the conditions are
+ * ORed ogether.
* <p>For example, domain_suffix_match=example.com would match test.example.com but would not
* match test-example.com.
* @param domain The domain value