Merge "Adjusted margins of Ongoing App Ops Dialog"
diff --git a/Android.bp b/Android.bp
index f40aab1..839d4cd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -296,6 +296,7 @@
"core/java/android/service/notification/IConditionListener.aidl",
"core/java/android/service/notification/IConditionProvider.aidl",
"core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+ "core/java/android/service/sms/IFinancialSmsService.aidl",
"core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
"core/java/android/service/vr/IVrListener.aidl",
"core/java/android/service/vr/IVrManager.aidl",
@@ -348,6 +349,7 @@
"core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
"core/java/android/view/autofill/IAutoFillManager.aidl",
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
+ "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
"core/java/android/view/intelligence/IIntelligenceManager.aidl",
"core/java/android/view/IApplicationToken.aidl",
@@ -724,6 +726,7 @@
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.3-java",
+ "android.hardware.radio-V1.4-java",
"android.hardware.usb.gadget-V1.0-java",
"netd_aidl_interface-java",
],
@@ -859,7 +862,7 @@
java_library {
name: "ext",
installable: true,
- sdk_version: "core_current",
+ no_framework_libs: true,
static_libs: [
"libphonenumber-platform",
"nist-sip",
diff --git a/api/current.txt b/api/current.txt
index abd3c31..14214cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -159,9 +159,6 @@
field public static final java.lang.String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS";
field public static final deprecated java.lang.String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
- field public static final java.lang.String WRITE_MEDIA_AUDIO = "android.permission.WRITE_MEDIA_AUDIO";
- field public static final java.lang.String WRITE_MEDIA_IMAGES = "android.permission.WRITE_MEDIA_IMAGES";
- field public static final java.lang.String WRITE_MEDIA_VIDEO = "android.permission.WRITE_MEDIA_VIDEO";
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
@@ -1319,6 +1316,7 @@
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
+ field public static final int supportsMultipleDisplays = 16844183; // 0x1010597
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
@@ -5229,6 +5227,7 @@
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public boolean getAllowSystemGeneratedContextualActions();
method public android.app.PendingIntent getAppOverlayIntent();
method public int getBadgeIconType();
method public java.lang.String getChannelId();
@@ -5460,6 +5459,7 @@
method public android.app.Notification.Style getStyle();
method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+ method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setBadgeIconType(int);
@@ -6352,6 +6352,7 @@
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
method public boolean supportsAmbientMode();
+ method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
}
@@ -10039,6 +10040,7 @@
field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
+ field public static final java.lang.String ACTION_DEFINE = "android.intent.action.DEFINE";
field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE";
field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -10129,6 +10131,7 @@
field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
+ field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
@@ -22639,7 +22642,7 @@
method public abstract void onLocationChanged(android.location.Location);
method public abstract void onProviderDisabled(java.lang.String);
method public abstract void onProviderEnabled(java.lang.String);
- method public abstract void onStatusChanged(java.lang.String, int, android.os.Bundle);
+ method public abstract deprecated void onStatusChanged(java.lang.String, int, android.os.Bundle);
}
public class LocationManager {
@@ -22651,7 +22654,7 @@
method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
method public void clearTestProviderEnabled(java.lang.String);
method public void clearTestProviderLocation(java.lang.String);
- method public void clearTestProviderStatus(java.lang.String);
+ method public deprecated void clearTestProviderStatus(java.lang.String);
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
method public java.lang.String getGnssHardwareModelName();
@@ -22688,7 +22691,7 @@
method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
method public void setTestProviderEnabled(java.lang.String, boolean);
method public void setTestProviderLocation(java.lang.String, android.location.Location);
- method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+ method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
@@ -22696,7 +22699,7 @@
field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering";
- field public static final java.lang.String KEY_STATUS_CHANGED = "status";
+ field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status";
field public static final java.lang.String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
field public static final java.lang.String NETWORK_PROVIDER = "network";
field public static final java.lang.String PASSIVE_PROVIDER = "passive";
@@ -22715,9 +22718,9 @@
method public boolean supportsAltitude();
method public boolean supportsBearing();
method public boolean supportsSpeed();
- field public static final int AVAILABLE = 2; // 0x2
- field public static final int OUT_OF_SERVICE = 0; // 0x0
- field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
+ field public static final deprecated int AVAILABLE = 2; // 0x2
+ field public static final deprecated int OUT_OF_SERVICE = 0; // 0x0
+ field public static final deprecated int TEMPORARILY_UNAVAILABLE = 1; // 0x1
}
public abstract interface OnNmeaMessageListener {
@@ -22946,7 +22949,7 @@
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
- method public static int generateAudioSessionId();
+ method public int generateAudioSessionId();
method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
@@ -24586,6 +24589,7 @@
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
+ field public static final java.lang.String MIMETYPE_AUDIO_AC4 = "audio/ac4";
field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
@@ -29128,15 +29132,18 @@
method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
- method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+ method public android.net.wifi.WifiNetworkConfigBuilder setIsEnhancedOpen();
method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
- method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa2EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa2Passphrase(java.lang.String);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa3EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public android.net.wifi.WifiNetworkConfigBuilder setWpa3Passphrase(java.lang.String);
}
public final class WifiNetworkSuggestion implements android.os.Parcelable {
@@ -42882,6 +42889,7 @@
field public static final java.lang.String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+ field public static final java.lang.String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
@@ -43229,9 +43237,9 @@
public class MbmsGroupCallSession implements java.lang.AutoCloseable {
method public void close();
- method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+ method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
- method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback);
}
public class MbmsStreamingSession implements java.lang.AutoCloseable {
@@ -44236,7 +44244,7 @@
public class GroupCall implements java.lang.AutoCloseable {
method public void close();
method public long getTmgi();
- method public void updateGroupCall(int[], int[]);
+ method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
@@ -44248,11 +44256,10 @@
field public static final int STATE_STOPPED = 1; // 0x1
}
- public class GroupCallCallback {
- ctor public GroupCallCallback();
- method public void onBroadcastSignalStrengthUpdated(int);
- method public void onError(int, java.lang.String);
- method public void onGroupCallStateChanged(int, int);
+ public abstract interface GroupCallCallback {
+ method public abstract void onBroadcastSignalStrengthUpdated(int);
+ method public abstract void onError(int, java.lang.String);
+ method public abstract void onGroupCallStateChanged(int, int);
field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
}
@@ -44292,6 +44299,11 @@
field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
}
+ public static class MbmsErrors.GroupCallErrors {
+ field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6
+ field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5
+ }
+
public static class MbmsErrors.InitializationErrors {
field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
@@ -44304,12 +44316,11 @@
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
- public class MbmsGroupCallSessionCallback {
- ctor public MbmsGroupCallSessionCallback();
- method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
- method public void onError(int, java.lang.String);
- method public void onMiddlewareReady();
- method public void onServiceInterfaceAvailable(java.lang.String, int);
+ public abstract interface MbmsGroupCallSessionCallback {
+ method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+ method public abstract void onError(int, java.lang.String);
+ method public abstract void onMiddlewareReady();
+ method public abstract void onServiceInterfaceAvailable(java.lang.String, int);
}
public class MbmsStreamingSessionCallback {
@@ -45610,9 +45621,14 @@
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
- public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+ public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
ctor public LineHeightSpan.Standard(int);
+ ctor public LineHeightSpan.Standard(android.os.Parcel);
method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+ method public int describeContents();
+ method public int getHeight();
+ method public int getSpanTypeId();
+ method public void writeToParcel(android.os.Parcel, int);
}
public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
@@ -49099,7 +49115,7 @@
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onProvideAutofillStructure(android.view.ViewStructure, int);
method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
- method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+ method public void onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
@@ -49315,6 +49331,8 @@
method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
+ method public void transformMatrixToGlobal(android.graphics.Matrix);
+ method public void transformMatrixToLocal(android.graphics.Matrix);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable);
method public final void updateDragShadow(android.view.View.DragShadowBuilder);
@@ -51907,6 +51925,7 @@
method public java.time.ZonedDateTime getTime();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+ field public static final android.app.Person PERSON_USER_LOCAL;
}
public static final class ConversationActions.Message.Builder {
@@ -52175,6 +52194,7 @@
field public static final int STATUS_LINKS_APPLIED = 0; // 0x0
field public static final int STATUS_NO_LINKS_APPLIED = 2; // 0x2
field public static final int STATUS_NO_LINKS_FOUND = 1; // 0x1
+ field public static final int STATUS_UNSUPPORTED_CHARACTER = 4; // 0x4
}
public static final class TextLinks.Builder {
@@ -54168,6 +54188,7 @@
ctor public ImageView(android.content.Context, android.util.AttributeSet);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int, int);
+ method public void animateTransform(android.graphics.Matrix);
method public final void clearColorFilter();
method public boolean getAdjustViewBounds();
method public boolean getBaselineAlignBottom();
diff --git a/api/system-current.txt b/api/system-current.txt
index 75a2cc4..a53ab3e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1225,7 +1225,6 @@
method public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
- method public android.content.pm.PackageInfo getPackageInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -4938,12 +4937,47 @@
package android.service.intelligence {
+ public final class FillCallback {
+ method public void onSuccess(android.service.intelligence.FillResponse);
+ }
+
+ public final class FillController {
+ method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+ }
+
+ public final class FillRequest {
+ method public android.view.autofill.AutofillId getFocusedId();
+ method public android.service.intelligence.PresentationParams getPresentationParams();
+ method public android.service.intelligence.InteractionSessionId getSessionId();
+ }
+
+ public final class FillResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.intelligence.FillResponse> CREATOR;
+ }
+
+ public static class FillResponse.Builder {
+ ctor public FillResponse.Builder();
+ method public android.service.intelligence.FillResponse build();
+ method public android.service.intelligence.FillResponse.Builder setFillWindow(android.service.intelligence.FillWindow);
+ method public android.service.intelligence.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+ }
+
+ public final class FillWindow {
+ ctor public FillWindow();
+ method public void destroy();
+ method public boolean update(android.service.intelligence.PresentationParams.Area, android.view.View, long);
+ field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+ }
+
public abstract class IntelligenceService extends android.app.Service {
ctor public IntelligenceService();
method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+ method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
}
@@ -4965,6 +4999,23 @@
field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
}
+ public abstract class PresentationParams {
+ method public int getFlags();
+ method public android.service.intelligence.PresentationParams.Area getFullArea();
+ method public android.service.intelligence.PresentationParams.Area getSuggestionArea();
+ field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+ field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+ field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+ field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+ field public static final int FLAG_HOST_IME = 16; // 0x10
+ field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+ }
+
+ public static abstract class PresentationParams.Area {
+ method public android.graphics.Rect getBounds();
+ method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
+ }
+
public final class SnapshotData implements android.os.Parcelable {
method public int describeContents();
method public android.app.assist.AssistContent getAssistContent();
@@ -5024,13 +5075,18 @@
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
}
public final class NotificationStats implements android.os.Parcelable {
@@ -5184,6 +5240,16 @@
}
+package android.service.sms {
+
+ public abstract class FinancialSmsService extends android.app.Service {
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.database.CursorWindow onGetSmsMessages(android.os.Bundle);
+ field public static final java.lang.String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+ }
+
+}
+
package android.service.textclassifier {
public abstract class TextClassifierService extends android.app.Service {
@@ -7023,9 +7089,9 @@
method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, int[], int[]);
+ method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
}
public class MbmsStreamingServiceBase extends android.os.Binder {
@@ -7517,7 +7583,7 @@
method public default void onMovedToDisplay(int, android.content.res.Configuration);
method public abstract void onOverScrolled(int, int, boolean, boolean);
method public default void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
- method public default boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+ method public default void onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
method public abstract void onScrollChanged(int, int, int, int);
method public abstract void onSizeChanged(int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c01cf1..738caec 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -985,6 +985,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+ field public static final java.lang.String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = "autofill_smart_suggestion_emulation_flags";
field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
@@ -1155,12 +1156,17 @@
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
+ method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+ field public static final int SOURCE_FROM_APP = 0; // 0x0
+ field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
}
public abstract class NotificationListenerService extends android.app.Service {
@@ -1318,9 +1324,9 @@
method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, int[], int[]);
+ method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
}
public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 5a6c813..7d675ce 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -18,7 +18,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"libidmap2/BinaryStreamVisitor.cpp",
@@ -64,7 +64,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
@@ -118,7 +118,7 @@
tidy: true,
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
"idmap2/Create.cpp",
@@ -165,7 +165,7 @@
],
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
+// b/120024673 "-warnings-as-errors=*",
],
srcs: [
":idmap2_aidl",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3ee0a06..e97d6c3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -205,6 +205,7 @@
DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
BatteryLevel battery_level = 10043;
+ BuildInformation build_information = 10044;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -2421,8 +2422,8 @@
*/
message CpuTimePerUid {
optional int32 uid = 1 [(is_uid) = true];
- optional uint64 user_time_millis = 2;
- optional uint64 sys_time_millis = 3;
+ optional uint64 user_time_micros = 2;
+ optional uint64 sys_time_micros = 3;
}
/**
@@ -3331,6 +3332,40 @@
}
/**
+ * Pulls information about the device's build.
+ */
+message BuildInformation {
+ // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse.
+ // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags.
+ optional string fingerprint = 1;
+
+ // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated.
+ optional string brand = 2;
+
+ // Build.PRODUCT. The name of the overall product.
+ optional string product = 3;
+
+ // Build.DEVICE. The name of the industrial design.
+ optional string device = 4;
+
+ // Build.VERSION.RELEASE. The user-visible version string. E.g., "1.0" or "3.4b5" or "bananas".
+ optional string version_release = 5;
+
+ // Build.ID. E.g. a label like "M4-rc20".
+ optional string id = 6;
+
+ // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to
+ // represent this build.
+ optional string version_incremental = 7;
+
+ // Build.TYPE. The type of build, like "user" or "eng".
+ optional string type = 8;
+
+ // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug".
+ optional string tags = 9;
+}
+
+/**
* Pulls on-device BatteryStats power use calculations for the overall device.
*/
message DeviceCalculatedPowerUse {
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ab635a0..87a065b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -265,6 +265,11 @@
{{}, {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+ // BuildInformation.
+ {android::util::BUILD_INFORMATION,
+ {{}, {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 343709a..3157037 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -113,7 +113,7 @@
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
- static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+ static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
// Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
// data subscriber that it's time to call getData.
@@ -130,7 +130,7 @@
static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
/* Min period between two checks of byte size per config key in nanoseconds. */
- static const int64_t kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC;
+ static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
// Maximum age (30 days) that files on disk can exist in seconds.
static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 2dcf50f..41c2e6c 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -41154,7 +41154,7 @@
HSPLcom/android/internal/telephony/TimeServiceHelper;->setListener(Lcom/android/internal/telephony/TimeServiceHelper$Listener;)V
HSPLcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;->toString()Ljava/lang/String;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;->toString()Ljava/lang/String;
-HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
+HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByCountry(Ljava/lang/String;J)Lcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;
HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByNitzCountry(Lcom/android/internal/telephony/NitzData;Ljava/lang/String;)Lcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;
HSPLcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastRangeForSubscriber(IIII)Z
@@ -51930,7 +51930,30 @@
HSPLlibcore/reflect/Types;->getTypeArray(Llibcore/reflect/ListOfTypes;Z)[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/WildcardTypeImpl;->getLowerBounds()[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/WildcardTypeImpl;->getUpperBounds()[Ljava/lang/reflect/Type;
-HSPLlibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
+HSPLlibcore/timezone/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
+HSPLlibcore/timezone/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
+HSPLlibcore/timezone/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
+HSPLlibcore/timezone/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/timezone/CountryTimeZones$OffsetResult;
+HSPLlibcore/timezone/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
+HSPLlibcore/timezone/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
+HSPLlibcore/timezone/TimeZoneFinder;->getInstance()Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
+HSPLlibcore/timezone/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
+HSPLlibcore/timezone/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
+HSPLlibcore/timezone/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->processXml(Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)V
HSPLlibcore/util/BasicLruCache;-><init>(I)V
HSPLlibcore/util/BasicLruCache;->create(Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/BasicLruCache;->entryEvicted(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -51938,12 +51961,6 @@
HSPLlibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/CollectionUtils;->removeDuplicates(Ljava/util/List;Ljava/util/Comparator;)V
-HSPLlibcore/util/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
-HSPLlibcore/util/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
-HSPLlibcore/util/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
-HSPLlibcore/util/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
-HSPLlibcore/util/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/util/CountryTimeZones$OffsetResult;
HSPLlibcore/util/HexEncoding;->encode([BII)[C
HSPLlibcore/util/NativeAllocationRegistry$CleanerRunner;->run()V
HSPLlibcore/util/NativeAllocationRegistry$CleanerThunk;->run()V
@@ -51951,23 +51968,6 @@
HSPLlibcore/util/NativeAllocationRegistry;->registerNativeAllocation(Ljava/lang/Object;J)Ljava/lang/Runnable;
HSPLlibcore/util/SneakyThrow;->sneakyThrow(Ljava/lang/Throwable;)V
HSPLlibcore/util/SneakyThrow;->sneakyThrow_(Ljava/lang/Throwable;)V
-HSPLlibcore/util/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
-HSPLlibcore/util/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/util/TimeZoneFinder$ReaderSupplier;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
-HSPLlibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
-HSPLlibcore/util/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
-HSPLlibcore/util/TimeZoneFinder;->getInstance()Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
-HSPLlibcore/util/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
-HSPLlibcore/util/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
-HSPLlibcore/util/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)Z
-HSPLlibcore/util/TimeZoneFinder;->processXml(Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)V
HSPLlibcore/util/ZoneInfo$WallTime;-><init>()V
HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsFromCalendar()V
HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsToCalendar()V
@@ -63455,13 +63455,18 @@
Llibcore/reflect/TypeVariableImpl;
Llibcore/reflect/Types;
Llibcore/reflect/WildcardTypeImpl;
-Llibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/CountryTimeZones$OffsetResult;
+Llibcore/timezone/CountryTimeZones$TimeZoneMapping;
+Llibcore/timezone/CountryTimeZones;
+Llibcore/timezone/TimeZoneDataFiles;
+Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+Llibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
+Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;
+Llibcore/timezone/TimeZoneFinder;
Llibcore/util/BasicLruCache;
Llibcore/util/CharsetUtils;
Llibcore/util/CollectionUtils;
-Llibcore/util/CountryTimeZones$OffsetResult;
-Llibcore/util/CountryTimeZones$TimeZoneMapping;
-Llibcore/util/CountryTimeZones;
Llibcore/util/EmptyArray;
Llibcore/util/HexEncoding;
Llibcore/util/NativeAllocationRegistry$CleanerRunner;
@@ -63469,11 +63474,6 @@
Llibcore/util/NativeAllocationRegistry;
Llibcore/util/Objects;
Llibcore/util/SneakyThrow;
-Llibcore/util/TimeZoneDataFiles;
-Llibcore/util/TimeZoneFinder$ReaderSupplier;
-Llibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
-Llibcore/util/TimeZoneFinder$TimeZonesProcessor;
-Llibcore/util/TimeZoneFinder;
Llibcore/util/ZoneInfo$CheckedArithmeticException;
Llibcore/util/ZoneInfo$OffsetInterval;
Llibcore/util/ZoneInfo$WallTime;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a770b9..f78506b 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -189,10 +189,10 @@
Landroid/app/IUiModeManager;->disableCarMode(I)V
Landroid/app/IUserSwitchObserver$Stub;-><init>()V
Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
-Landroid/app/IWallpaperManager;->getHeightHint()I
+Landroid/app/IWallpaperManager;->getHeightHint(I)I
Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
Landroid/app/IWallpaperManager;->getWallpaperInfo(I)Landroid/app/WallpaperInfo;
-Landroid/app/IWallpaperManager;->getWidthHint()I
+Landroid/app/IWallpaperManager;->getWidthHint(I)I
Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z
Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V
Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V
@@ -1488,86 +1488,10 @@
Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
Landroid/widget/ScrollBarDrawable;-><init>()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->clear()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->getRememberedPosition()I
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigit(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigitAndRememberPosition(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getDescriptionForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/util/Locale;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getInstance()Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;
-Lcom/android/i18n/phonenumbers/NumberParseException;->getErrorType()Lcom/android/i18n/phonenumbers/NumberParseException$ErrorType;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getDomesticCarrierCodeFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getFormat()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getLeadingDigitsPattern(I)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getNationalPrefixFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->leadingDigitsPatternSize()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getGeneralDesc()Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixForParsing()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixTransformRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getPreferredExtnPrefix()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasNationalPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasPreferredExtnPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->intlNumberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->numberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;->getMetadataList()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getNationalNumberPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_DEFAULT_COUNTRY:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITHOUT_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_IDD:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->clearCountryCode()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCodeSource()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getExtension()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getNationalNumber()J
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasCountryCode()Z
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasExtension()Z
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->end()I
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->number()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->rawString()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->start()I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;->POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->EXACT_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NOT_A_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NO_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->SHORT_NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->E164:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->INTERNATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->NATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->RFC3966:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE_OR_MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PAGER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PERSONAL_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PREMIUM_RATE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->SHARED_COST:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->TOLL_FREE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->UAN:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOICEMAIL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOIP:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->IS_POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->TOO_LONG:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->findNumbers(Ljava/lang/CharSequence;Ljava/lang/String;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;J)Ljava/lang/Iterable;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->format(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->formatInOriginalFormat(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getAsYouTypeFormatter(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getCountryCodeForRegion(Ljava/lang/String;)I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getInstance()Lcom/android/i18n/phonenumbers/PhoneNumberUtil;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNationalSignificantNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNumberType(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getRegionCodeForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isNumberMatch(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumberWithReason(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isValidNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
Lcom/android/ims/ImsCall;->isMultiparty()Z
Lcom/android/ims/ImsCall;->reject(I)V
@@ -4139,77 +4063,6 @@
Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrollStateChanged(I)V
Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageSelected(I)V
Lcom/android/internal/widget/ViewPager;->getCurrentItem()I
-Lcom/android/okhttp/Connection;->getSocket()Ljava/net/Socket;
-Lcom/android/okhttp/ConnectionPool;->connections:Ljava/util/Deque;
-Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
-Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
-Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/HttpHandler;-><init>()V
-Lcom/android/okhttp/HttpsHandler;-><init>()V
-Lcom/android/okhttp/HttpUrl$Builder;->build()Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
-Lcom/android/okhttp/HttpUrl;->newBuilder()Lcom/android/okhttp/HttpUrl$Builder;
-Lcom/android/okhttp/HttpUrl;->parse(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HeaderParser;->skipUntil(Ljava/lang/String;ILjava/lang/String;)I
-Lcom/android/okhttp/internal/http/HeaderParser;->skipWhitespace(Ljava/lang/String;I)I
-Lcom/android/okhttp/internal/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Lcom/android/okhttp/internal/http/HttpEngine;->getConnection()Lcom/android/okhttp/Connection;
-Lcom/android/okhttp/internal/http/HttpEngine;->hasResponse()Z
-Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest:Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->readResponse()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sendRequest()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sentRequestMillis:J
-Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->writingRequestHeaders()V
-Lcom/android/okhttp/internal/http/RouteSelector;->hasNext()Z
-Lcom/android/okhttp/internal/huc/HttpsURLConnectionImpl;->delegate:Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->client:Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->httpEngine:Lcom/android/okhttp/internal/http/HttpEngine;
-Lcom/android/okhttp/internal/Internal;-><init>()V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->apply(Lcom/android/okhttp/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V
-Lcom/android/okhttp/internal/Internal;->callEngineGetStreamAllocation(Lcom/android/okhttp/Call;)Lcom/android/okhttp/internal/http/StreamAllocation;
-Lcom/android/okhttp/internal/Internal;->callEnqueue(Lcom/android/okhttp/Call;Lcom/android/okhttp/Callback;Z)V
-Lcom/android/okhttp/internal/Internal;->connectionBecameIdle(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)Z
-Lcom/android/okhttp/internal/Internal;->get(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/Address;Lcom/android/okhttp/internal/http/StreamAllocation;)Lcom/android/okhttp/internal/io/RealConnection;
-Lcom/android/okhttp/internal/Internal;->getHttpUrlChecked(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/internal/Internal;->instance:Lcom/android/okhttp/internal/Internal;
-Lcom/android/okhttp/internal/Internal;->internalCache(Lcom/android/okhttp/OkHttpClient;)Lcom/android/okhttp/internal/InternalCache;
-Lcom/android/okhttp/internal/Internal;->put(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)V
-Lcom/android/okhttp/internal/Internal;->routeDatabase(Lcom/android/okhttp/ConnectionPool;)Lcom/android/okhttp/internal/RouteDatabase;
-Lcom/android/okhttp/internal/Internal;->setCache(Lcom/android/okhttp/OkHttpClient;Lcom/android/okhttp/internal/InternalCache;)V
-Lcom/android/okhttp/internal/Platform;->get()Lcom/android/okhttp/internal/Platform;
-Lcom/android/okhttp/internal/Platform;->logW(Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Util;->closeAll(Ljava/io/Closeable;Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->closeQuietly(Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->EMPTY_BYTE_ARRAY:[B
-Lcom/android/okhttp/internal/Util;->UTF_8:Ljava/nio/charset/Charset;
-Lcom/android/okhttp/OkHttpClient;-><init>()V
-Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
-Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
-Lcom/android/okhttp/OkHttpClient;->getConnectionPool()Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->getCookieHandler()Ljava/net/CookieHandler;
-Lcom/android/okhttp/OkHttpClient;->getHostnameVerifier()Ljavax/net/ssl/HostnameVerifier;
-Lcom/android/okhttp/OkHttpClient;->getProxy()Ljava/net/Proxy;
-Lcom/android/okhttp/OkHttpClient;->getProxySelector()Ljava/net/ProxySelector;
-Lcom/android/okhttp/OkHttpClient;->getSslSocketFactory()Ljavax/net/ssl/SSLSocketFactory;
-Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
-Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Request;->method:Ljava/lang/String;
-Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/Response;->code:I
-Lcom/android/okhttp/Response;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Response;->message:Ljava/lang/String;
-Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 63a41ec..3069be6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1180,11 +1180,11 @@
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.WRITE_MEDIA_AUDIO,
+ null, // no permission for OP_WRITE_MEDIA_AUDIO
Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.WRITE_MEDIA_VIDEO,
+ null, // no permission for OP_WRITE_MEDIA_VIDEO
Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.WRITE_MEDIA_IMAGES,
+ null, // no permission for OP_WRITE_MEDIA_IMAGES
};
/**
@@ -1462,11 +1462,11 @@
AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_AUDIO
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_VIDEO
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
- AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_IMAGES
+ AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
};
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8bb704d..8a797dc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2046,8 +2046,6 @@
StorageManager storage) {
if (app.isInternal()) {
return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
- } else if (app.isExternalAsec()) {
- return storage.getPrimaryPhysicalVolume();
} else {
return storage.findVolumeByUuid(app.volumeUuid);
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5ef4be1..00547b4 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -87,24 +87,24 @@
/**
* Sets the dimension hint for the wallpaper. These hints indicate the desired
- * minimum width and height for the wallpaper.
+ * minimum width and height for the wallpaper in a particular display.
*/
- void setDimensionHints(in int width, in int height, in String callingPackage);
+ void setDimensionHints(in int width, in int height, in String callingPackage, int displayId);
/**
- * Returns the desired minimum width for the wallpaper.
+ * Returns the desired minimum width for the wallpaper in a particular display.
*/
- int getWidthHint();
+ int getWidthHint(int displayId);
/**
- * Returns the desired minimum height for the wallpaper.
+ * Returns the desired minimum height for the wallpaper in a particular display.
*/
- int getHeightHint();
+ int getHeightHint(int displayId);
/**
* Sets extra padding that we would like the wallpaper to have outside of the display.
*/
- void setDisplayPadding(in Rect padding, in String callingPackage);
+ void setDisplayPadding(in Rect padding, in String callingPackage, int displayId);
/**
* Returns the name of the wallpaper. Private API.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f2a3e44..75b56f3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1338,6 +1338,11 @@
private int mBadgeIcon = BADGE_ICON_NONE;
/**
+ * Determines whether the platform can generate contextual actions for a notification.
+ */
+ private boolean mAllowSystemGeneratedContextualActions = true;
+
+ /**
* Structure to encapsulate a named action that can be shown as part of this notification.
* It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
* selected by the user.
@@ -2238,6 +2243,8 @@
if (parcel.readInt() != 0) {
mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
}
+
+ mAllowSystemGeneratedContextualActions = parcel.readBoolean();
}
@Override
@@ -2353,6 +2360,7 @@
that.mSettingsText = this.mSettingsText;
that.mGroupAlertBehavior = this.mGroupAlertBehavior;
that.mAppOverlayIntent = this.mAppOverlayIntent;
+ that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
if (!heavy) {
that.lightenPayload(); // will clean out extras
@@ -2681,6 +2689,8 @@
parcel.writeInt(0);
}
+ parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+
// mUsesStandardHeader is not written because it should be recomputed in listeners
}
@@ -3101,6 +3111,10 @@
return mAppOverlayIntent;
}
+ public boolean getAllowSystemGeneratedContextualActions() {
+ return mAllowSystemGeneratedContextualActions;
+ }
+
/**
* The small icon representing this notification in the status bar and content view.
*
@@ -5657,6 +5671,15 @@
}
/**
+ * Determines whether the platform can generate contextual actions for a notification.
+ * By default this is true.
+ */
+ public Builder setAllowSystemGeneratedContextualActions(boolean allowed) {
+ mN.mAllowSystemGeneratedContextualActions = allowed;
+ return this;
+ }
+
+ /**
* @deprecated Use {@link #build()} instead.
*/
@Deprecated
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index a2dae3b..0abc998 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -127,8 +127,8 @@
if (obj instanceof Person) {
final Person other = (Person) obj;
return Objects.equals(mName, other.mName)
- && mIcon == null ? other.mIcon == null :
- (other.mIcon != null && mIcon.sameAs(other.mIcon))
+ && (mIcon == null ? other.mIcon == null :
+ (other.mIcon != null && mIcon.sameAs(other.mIcon)))
&& Objects.equals(mUri, other.mUri)
&& Objects.equals(mKey, other.mKey)
&& mIsBot == other.mIsBot
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index e33d1fe..3ea3da2 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -36,6 +36,7 @@
import android.util.AttributeSet;
import android.util.Printer;
import android.util.Xml;
+import android.view.SurfaceHolder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -79,6 +80,7 @@
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
final String mSettingsSliceUri;
+ final boolean mSupportMultipleDisplays;
/**
* Constructor.
@@ -143,6 +145,9 @@
false);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
+ mSupportMultipleDisplays = sa.getBoolean(
+ com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
+ false);
sa.recycle();
} catch (NameNotFoundException e) {
@@ -163,6 +168,7 @@
mShowMetadataInPreview = source.readInt() != 0;
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
+ mSupportMultipleDisplays = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -358,6 +364,19 @@
return Uri.parse(mSettingsSliceUri);
}
+ /**
+ * Returns whether this wallpaper service can support multiple engines to render on each surface
+ * independently. An example use case is a multi-display set-up where the wallpaper service can
+ * render surfaces to each of the connected displays.
+ *
+ * @see WallpaperService#onCreateEngine()
+ * @see WallpaperService.Engine#onCreate(SurfaceHolder)
+ * @return {@code true} if multiple engines can render independently on each surface.
+ */
+ public boolean supportsMultipleDisplays() {
+ return mSupportMultipleDisplays;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Service:");
mService.dump(pw, prefix + " ");
@@ -387,6 +406,7 @@
dest.writeInt(mShowMetadataInPreview ? 1 : 0);
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
+ dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index bebe79e..27471ca 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1485,7 +1485,7 @@
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.getWidthHint();
+ return sGlobals.mService.getWidthHint(mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1511,7 +1511,7 @@
throw new RuntimeException(new DeadSystemException());
}
try {
- return sGlobals.mService.getHeightHint();
+ return sGlobals.mService.getHeightHint(mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1572,7 +1572,7 @@
throw new RuntimeException(new DeadSystemException());
} else {
sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getDisplayId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1597,7 +1597,8 @@
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName());
+ sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(),
+ mContext.getDisplayId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5514851..3f34803 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -193,10 +193,6 @@
/** @hide */
public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
/** @hide */
- public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E;
- /** @hide */
- public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F;
- /** @hide */
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 75f5b17..e7f0053 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3589,6 +3589,27 @@
public static final String
ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
+
+ /**
+ * Activity Action: Perform text translation.
+ * <p>
+ * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
+
+ /**
+ * Activity Action: Define the meaning of the selected word(s).
+ * <p>
+ * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
/**
* Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
* exisiting sensor being disconnected.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4e110da..7c3b5e4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,7 +32,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.text.TextUtils;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -464,16 +463,6 @@
public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
/**
- * Value for {@link #privateFlags}: Set to true if the application has been
- * installed using the forward lock option.
- *
- * NOTE: DO NOT CHANGE THIS VALUE! It is saved in packages.xml.
- *
- * {@hide}
- */
- public static final int PRIVATE_FLAG_FORWARD_LOCK = 1<<2;
-
- /**
* Value for {@link #privateFlags}: set to {@code true} if the application
* is permitted to hold privileged permissions.
*
@@ -651,7 +640,6 @@
PRIVATE_FLAG_CANT_SAVE_STATE,
PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
PRIVATE_FLAG_DIRECT_BOOT_AWARE,
- PRIVATE_FLAG_FORWARD_LOCK,
PRIVATE_FLAG_HAS_DOMAIN_URLS,
PRIVATE_FLAG_HIDDEN,
PRIVATE_FLAG_INSTANT,
@@ -1843,17 +1831,6 @@
return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- /** @hide */
- public boolean isExternalAsec() {
- return TextUtils.isEmpty(volumeUuid) && isExternal();
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public boolean isForwardLocked() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
-
/**
* True if the application is installed as an instant app.
* @hide
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f90199..07672d9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1374,12 +1374,6 @@
}
/** {@hide} */
- public void setInstallFlagsInternal() {
- installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
- }
-
- /** {@hide} */
@SystemApi
public void setAllowDowngrade(boolean allowDowngrade) {
if (allowDowngrade) {
@@ -1390,12 +1384,6 @@
}
/** {@hide} */
- public void setInstallFlagsExternal() {
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- installFlags &= ~PackageManager.INSTALL_INTERNAL;
- }
-
- /** {@hide} */
public void setInstallFlagsForcePermissionPrompt() {
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ad06be3..a4b724b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -700,10 +700,8 @@
/** @hide */
@IntDef(flag = true, prefix = { "INSTALL_" }, value = {
- INSTALL_FORWARD_LOCK,
INSTALL_REPLACE_EXISTING,
INSTALL_ALLOW_TEST,
- INSTALL_EXTERNAL,
INSTALL_INTERNAL,
INSTALL_FROM_ADB,
INSTALL_ALL_USERS,
@@ -721,17 +719,6 @@
public @interface InstallFlags {}
/**
- * Flag parameter for {@link #installPackage} to indicate that this package
- * should be installed as forward locked, i.e. only the app itself should
- * have access to its code and non-resource assets.
- *
- * @deprecated new installs into ASEC containers are no longer supported.
- * @hide
- */
- @Deprecated
- public static final int INSTALL_FORWARD_LOCK = 0x00000001;
-
- /**
* Flag parameter for {@link #installPackage} to indicate that you want to
* replace an already installed package, if one exists.
*
@@ -750,17 +737,6 @@
/**
* Flag parameter for {@link #installPackage} to indicate that this package
- * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
- *
- * @deprecated new installs into ASEC containers are no longer supported;
- * use adoptable storage instead.
- * @hide
- */
- @Deprecated
- public static final int INSTALL_EXTERNAL = 0x00000008;
-
- /**
- * Flag parameter for {@link #installPackage} to indicate that this package
* must be installed to internal storage.
*
* @hide
@@ -1521,14 +1497,6 @@
/**
* Error code that is passed to the {@link IPackageMoveObserver} if the
- * specified package cannot be moved since its forward locked.
- *
- * @hide
- */
- public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
-
- /**
- * Error code that is passed to the {@link IPackageMoveObserver} if the
* specified package cannot be moved to the specified location.
*
* @hide
@@ -3143,33 +3111,6 @@
@PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
/**
- * Retrieve overall information about an application package that is
- * installed on the system.
- *
- * @param packageName The full name (i.e. com.google.apps.contacts) of the
- * desired package.
- * @param flags Additional option flags to modify the data returned.
- * @param userHandle The user.
- * @return A PackageInfo object containing information about the package. If
- * flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package
- * is not found in the list of installed applications, the package
- * information is retrieved from the list of uninstalled
- * applications (which includes installed applications as well as
- * applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
- * @throws NameNotFoundException if a package with the given name cannot be
- * found on the system.
- * @hide
- */
- @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
- @SystemApi
- public @NonNull PackageInfo getPackageInfoAsUser(@NonNull String packageName,
- @PackageInfoFlags int flags,
- @NonNull UserHandle userHandle) throws NameNotFoundException {
- return getPackageInfoAsUser(packageName, flags, userHandle.getIdentifier());
- }
-
- /**
* Map from the current package names in use on the device to whatever
* the current canonical name of that package is.
* @param names Array of current names to be mapped.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2fcf1dd..c78960b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -829,9 +829,6 @@
public static final int PARSE_MUST_BE_APK = 1 << 0;
public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
- /** @deprecated forward lock no longer functional. remove. */
- @Deprecated
- public static final int PARSE_FORWARD_LOCK = 1 << 2;
public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
@@ -845,7 +842,6 @@
PARSE_ENFORCE_CODE,
PARSE_EXTERNAL_STORAGE,
PARSE_FORCE_SDK,
- PARSE_FORWARD_LOCK,
PARSE_IGNORE_PROCESSES,
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
@@ -2006,11 +2002,6 @@
PARSE_DEFAULT_TARGET_SANDBOX);
pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
- /* Set the global "forward lock" flag */
- if ((flags & PARSE_FORWARD_LOCK) != 0) {
- pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
- }
-
/* Set the global "on SD card" flag */
if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -2532,55 +2523,33 @@
final ArraySet<String> newPermissions = new ArraySet<>();
newPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
newPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
newPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- newPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
newPermissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION);
newPermissions.add(android.Manifest.permission.WRITE_OBB);
- final ArraySet<String> dangerousPermissions = new ArraySet<>();
- dangerousPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
- dangerousPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ final ArraySet<String> removedPermissions = new ArraySet<>();
+ removedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
+ removedPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
for (int i = pkg.permissions.size() - 1; i >= 0; i--) {
final Permission p = pkg.permissions.get(i);
if (newPermissions.contains(p.info.name)) {
pkg.permissions.remove(i);
- } else if (dangerousPermissions.contains(p.info.name)) {
- p.info.protectionLevel &= ~PermissionInfo.PROTECTION_MASK_BASE;
- p.info.protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+ } else if (removedPermissions.contains(p.info.name)) {
+ p.info.flags &= ~PermissionInfo.FLAG_REMOVED;
}
}
}
} else {
if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
}
if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
}
if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
- }
-
- if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_LEGACY, false)) {
- if (pkg.requestedPermissions
- .contains(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
- }
- if (pkg.requestedPermissions
- .contains(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
- pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
- }
}
}
@@ -6801,7 +6770,7 @@
/** @hide */
public boolean isForwardLocked() {
- return applicationInfo.isForwardLocked();
+ return false;
}
/** @hide */
@@ -6843,9 +6812,7 @@
public boolean canHaveOatDir() {
// The following app types CANNOT have oat directory
// - non-updated system apps
- // - forward-locked apps or apps installed in ASEC containers
- return (!isSystem() || isUpdatedSystemApp())
- && !isForwardLocked() && !applicationInfo.isExternalAsec();
+ return !isSystem() || isUpdatedSystemApp();
}
public boolean isMatch(int flags) {
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 720c167..97d72f0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -360,6 +360,16 @@
}
/**
+ * Returns the number of binder proxies held in this process.
+ * @return number of binder proxies in this process
+ */
+ public static int getProxyCount() {
+ synchronized (sProxyMap) {
+ return sProxyMap.size();
+ }
+ }
+
+ /**
* Dump proxy debug information.
*
* @hide
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8c5c415..900b62d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -54,7 +54,7 @@
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
- private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+ private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 423ce77..b42f1c4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -137,8 +137,6 @@
public static final String PROP_FORCE_VIDEO = "persist.fw.force_video";
/** {@hide} */
public static final String PROP_FORCE_IMAGES = "persist.fw.force_images";
- /** {@hide} */
- public static final String PROP_FORCE_LEGACY = "persist.fw.force_legacy";
/** {@hide} */
public static final String UUID_PRIVATE_INTERNAL = null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d579f0f..dbdeb70 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9339,6 +9339,13 @@
"location_background_throttle_package_whitelist";
/**
+ * Whether to disable location status callbacks in preparation for deprecation.
+ * @hide
+ */
+ public static final String LOCATION_DISABLE_STATUS_CALLBACKS =
+ "location_disable_status_callbacks";
+
+ /**
* Maximum staleness allowed for last location when returned to clients with only foreground
* location permissions.
* @hide
@@ -12701,6 +12708,17 @@
public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
/**
+ * Used to emulate Smart Suggestion for Augmented Autofill during development
+ *
+ * <p>Valid values: {@code 0x1} for IME and/or {@code 0x2} for popup window.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS =
+ "autofill_smart_suggestion_emulation_flags";
+
+ /**
* Exemptions to the hidden API blacklist.
*
* @hide
diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java
new file mode 100644
index 0000000..af2da79
--- /dev/null
+++ b/core/java/android/service/intelligence/FillCallback.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Callback used to indicate at {@link FillRequest} has been fulfilled.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillCallback {
+
+ FillCallback() {}
+
+ /**
+ * Sets the response associated with the request.
+ *
+ * @param response response associated with the request, or {@code null} if the service
+ * could not provide autofill for the request.
+ */
+ public void onSuccess(@Nullable FillResponse response) {
+ final FillWindow fillWindow = response.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.show();
+ }
+ // TODO(b/111330312): properly implement on server-side by updating the Session state
+ // accordingly (and adding CTS tests)
+ }
+}
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java
new file mode 100644
index 0000000..c5e1242
--- /dev/null
+++ b/core/java/android/service/intelligence/FillController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Object used to interact with the autofill system.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillController {
+ private static final String TAG = "FillController";
+
+ private final AutofillProxy mProxy;
+
+ FillController(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * Fills the activity with the provided values.
+ *
+ * <p>As a side effect, the {@link FillWindow} associated with the {@link FillResponse} will be
+ * automatically {@link FillWindow#destroy() destroyed}.
+ */
+ public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
+ Preconditions.checkNotNull(values);
+
+ if (DEBUG) {
+ Log.d(TAG, "autofill() with " + values.size() + " values");
+ }
+
+ try {
+ mProxy.autofill(values);
+ final FillWindow fillWindow = mProxy.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.destroy();
+ }
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
new file mode 100644
index 0000000..95e9224
--- /dev/null
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.view.autofill.AutofillId;
+
+/**
+ * Represents a request to augment-fill an activity.
+ * @hide
+ */
+@SystemApi
+public final class FillRequest {
+
+ final AutofillProxy mProxy;
+
+ /** @hide */
+ FillRequest(@NonNull AutofillProxy proxy) {
+ mProxy = proxy;
+ }
+
+ /**
+ * Gets the session associated with this request.
+ */
+ @NonNull
+ public InteractionSessionId getSessionId() {
+ return mProxy.sessionId;
+ }
+
+ /**
+ * Gets the id of the field that triggered the request.
+ */
+ @NonNull
+ public AutofillId getFocusedId() {
+ return mProxy.focusedId;
+ }
+
+ /**
+ * Gets the Smart Suggestions object used to embed the autofill UI.
+ *
+ * @return object used to embed the autofill UI, or {@code null} if not supported.
+ */
+ @Nullable
+ public PresentationParams getPresentationParams() {
+ return mProxy.getSmartSuggestionParams();
+ }
+
+ @Override
+ public String toString() {
+ return "FillRequest[id=" + mProxy.focusedId + "]";
+ }
+}
diff --git a/core/java/android/service/intelligence/FillResponse.java b/core/java/android/service/intelligence/FillResponse.java
new file mode 100644
index 0000000..860c027
--- /dev/null
+++ b/core/java/android/service/intelligence/FillResponse.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.util.List;
+
+/**
+ * Response to a {@link FillRequest}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillResponse implements Parcelable {
+
+ private final FillWindow mFillWindow;
+
+ private FillResponse(@NonNull Builder builder) {
+ mFillWindow = builder.mFillWindow;
+ }
+
+ /** @hide */
+ @Nullable
+ FillWindow getFillWindow() {
+ return mFillWindow;
+ }
+
+ /**
+ * Builder for {@link FillResponse} objects.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static class Builder {
+
+ private FillWindow mFillWindow;
+
+ /**
+ * Sets the {@link FillWindow} used to display the Autofill UI.
+ *
+ * <p>Must be called when the service is handling the request so the Android System can
+ * properly synchronize the UI.
+ *
+ * @return this builder
+ */
+ public Builder setFillWindow(@NonNull FillWindow fillWindow) {
+ // TODO(b/111330312): implement / check not null / unit test
+ // TODO(b/111330312): throw exception if FillWindow not updated yet
+ mFillWindow = fillWindow;
+ return this;
+ }
+
+ /**
+ * Tells the Android System that the given {@code ids} should not trigger further
+ * {@link FillRequest requests} when focused.
+ *
+ * @param ids ids of the fields that should be ignored
+ *
+ * @return this builder
+ */
+ public Builder setIgnoredIds(@NonNull List<AutofillId> ids) {
+ // TODO(b/111330312): implement / check not null / unit test
+ return this;
+ }
+
+ /**
+ * Builds a new {@link FillResponse} instance.
+ *
+ * @throws IllegalStateException if any of the following conditions occur:
+ * <ol>
+ * <li>{@link #build()} was already called.
+ * <li>No call was made to {@link #setFillWindow(FillWindow)} or
+ * {@ling #setIgnoredIds(List<AutofillId>)}.
+ * </ol>
+ *
+ * @return A built response.
+ */
+ public FillResponse build() {
+ // TODO(b/111330312): check conditions / add unit test
+ return new FillResponse(this);
+ }
+
+ // TODO(b/111330312): add methods to disable app / activity, either here or on manager
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ // TODO(b/111330312): implement
+ }
+
+ public static final Parcelable.Creator<FillResponse> CREATOR =
+ new Parcelable.Creator<FillResponse>() {
+
+ @Override
+ public FillResponse createFromParcel(Parcel parcel) {
+ // TODO(b/111330312): implement
+ return null;
+ }
+
+ @Override
+ public FillResponse[] newArray(int size) {
+ return new FillResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
new file mode 100644
index 0000000..4ea07bf
--- /dev/null
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Dialog;
+import android.graphics.Rect;
+import android.service.intelligence.PresentationParams.Area;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handle to a window used to display the augmented autofill UI.
+ *
+ * <p>The steps to create an augmented autofill UI are:
+ *
+ * <ol>
+ * <li>Gets the {@link PresentationParams} from the {@link FillRequest}.
+ * <li>Gets the {@link Area} to display the UI (for example, through
+ * {@link PresentationParams#getSuggestionArea()}.
+ * <li>Creates a {@link View} that must fit in the {@link Area#getBounds() area boundaries}.
+ * <li>Set the proper listeners to the view (for example, a click listener that
+ * triggers {@link FillController#autofill(java.util.List)}
+ * <li>Call {@link #update(Area, View, long)} with these arguments.
+ * <li>Create a {@link FillResponse} with the {@link FillWindow}.
+ * <li>Pass such {@link FillResponse} to {@link FillCallback#onSuccess(FillResponse)}.
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillWindow {
+ private static final String TAG = "FillWindow";
+
+ /** Indicates the data being shown is a physical address */
+ public static final long FLAG_METADATA_ADDRESS = 0x1;
+
+ // TODO(b/111330312): add moar flags
+
+ /** @hide */
+ @LongDef(prefix = { "FLAG" }, value = {
+ FLAG_METADATA_ADDRESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flags{}
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private Dialog mDialog;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed;
+
+ /**
+ * Updates the content of the window.
+ *
+ * @param rootView new root view
+ * @param area coordinates to render the view.
+ * @param flags optional flags such as metadata of what will be rendered in the window. The
+ * Smart Suggestion host might decide whether or not to render the UI based on them.
+ *
+ * @return boolean whether the window was updated or not.
+ *
+ * @throws IllegalArgumentException if the area is not compatible with this window
+ */
+ public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) {
+ if (DEBUG) {
+ Log.d(TAG, "Updating " + area + " + with " + rootView);
+ }
+ // TODO(b/111330312): add test case for null
+ Preconditions.checkNotNull(area);
+ Preconditions.checkNotNull(rootView);
+ // TODO(b/111330312): must check the area is a valid object returned by
+ // SmartSuggestionParams, throw IAE if not
+
+ // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider
+
+
+ // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating
+ // the window underneath the existing view.
+
+ final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams();
+ if (smartSuggestion == null) {
+ Log.w(TAG, "No SmartSuggestionParams");
+ return false;
+ }
+
+ final Rect rect = area.getBounds();
+ if (rect == null) {
+ Log.wtf(TAG, "No Rect on SmartSuggestionParams");
+ return false;
+ }
+
+ synchronized (mLock) {
+ checkNotDestroyedLocked();
+
+ // TODO(b/111330312): once we have the SurfaceControl approach, we should update the
+ // window instead of destroying. In fact, it might be better to allocate a full window
+ // initially, which is transparent (and let touches get through) everywhere but in the
+ // rect boundaries.
+ destroy();
+
+ // TODO(b/111330312): make sure all touch events are handled, window is always closed,
+ // etc.
+
+ mDialog = new Dialog(rootView.getContext());
+ final Window window = mDialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+
+ final int height = rect.bottom - rect.top;
+ final int width = rect.right - rect.left;
+ final WindowManager.LayoutParams windowParams = window.getAttributes();
+ windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ windowParams.y = rect.top - height;
+ windowParams.height = height;
+ windowParams.x = rect.left;
+ windowParams.width = width;
+
+ window.setAttributes(windowParams);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
+ mDialog.setContentView(rootView, diagParams);
+
+ if (DEBUG) {
+ Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
+ }
+
+ area.proxy.setFillWindow(this);
+ return true;
+ }
+ }
+
+ /** @hide */
+ void show() {
+ // TODO(b/111330312): check if updated first / throw exception
+ if (DEBUG) Log.d(TAG, "show()");
+
+ synchronized (mLock) {
+ checkNotDestroyedLocked();
+ if (mDialog == null) {
+ throw new IllegalStateException("update() not called yet, or already destroyed()");
+ }
+
+ mDialog.show();
+ }
+ }
+
+ /**
+ * Destroys the window.
+ *
+ * <p>Once destroyed, this window cannot be used anymore
+ */
+ public void destroy() {
+ if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed);
+
+ synchronized (this) {
+ if (mDestroyed) return;
+
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+ }
+
+ private void checkNotDestroyedLocked() {
+ if (mDestroyed) {
+ throw new IllegalStateException("already destroyed()");
+ }
+ }
+
+ /** @hide */
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ synchronized (this) {
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ if (mDialog != null) {
+ pw.print(prefix); pw.print("dialog: ");
+ pw.println(mDialog.isShowing() ? "shown" : "hidden");
+ pw.print(prefix); pw.print("window: ");
+ pw.println(mDialog.getWindow().getAttributes());
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index 709c3b7..e2260d7 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -16,10 +16,12 @@
package android.service.intelligence;
+import android.os.IBinder;
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.InteractionContext;
import android.service.intelligence.SnapshotData;
+import android.view.autofill.AutofillId;
import android.view.intelligence.ContentCaptureEvent;
import java.util.List;
@@ -40,4 +42,9 @@
void onActivitySnapshot(in InteractionSessionId sessionId,
in SnapshotData snapshotData);
+
+ void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient,
+ int autofilSessionId, in AutofillId focusedId);
+
+ void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId);
}
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
index 27569b6..040e25e 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -22,13 +22,26 @@
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.autofill.IAugmentedAutofillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -44,6 +57,9 @@
private static final String TAG = "IntelligenceService";
+ // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+ static final boolean DEBUG = true;
+
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
@@ -55,6 +71,8 @@
private Handler mHandler;
+ private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies;
+
private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
@Override
@@ -87,6 +105,20 @@
obtainMessage(IntelligenceService::onActivitySnapshot,
IntelligenceService.this, sessionId, snapshotData));
}
+
+ @Override
+ public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
+ int autofilSessionId, AutofillId focusedId) {
+ mHandler.sendMessage(obtainMessage(IntelligenceService::handleOnAutofillRequest,
+ IntelligenceService.this, sessionId, client, autofilSessionId, focusedId));
+ }
+
+ @Override
+ public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
+ mHandler.sendMessage(
+ obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest,
+ IntelligenceService.this, sessionId));
+ }
};
@CallSuper
@@ -122,10 +154,93 @@
* @param sessionId the session's Id
* @param events the events
*/
- // TODO(b/111276913): rename to onContentCaptureEvents
+ // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
+ // Request object so it can be extended
public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
@NonNull List<ContentCaptureEvent> events);
+ private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
+ @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
+ if (mAutofillProxies == null) {
+ mAutofillProxies = new ArrayMap<>();
+ }
+ AutofillProxy proxy = mAutofillProxies.get(sessionId);
+ if (proxy == null) {
+ proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId);
+ mAutofillProxies.put(sessionId, proxy);
+ } else {
+ // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+ if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+ }
+ // TODO(b/111330312): set cancellation signal
+ final CancellationSignal cancellationSignal = null;
+ onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
+ new FillController(proxy), new FillCallback());
+ }
+
+ /**
+ * Asks the service to handle an "augmented" autofill request.
+ *
+ * <p>This method is called when the "stantard" autofill service cannot handle a request, which
+ * typically occurs when:
+ * <ul>
+ * <li>Service does not recognize what should be autofilled.
+ * <li>Service does not have data to fill the request.
+ * <li>Service blacklisted that app (or activity) for autofill.
+ * <li>App disabled itself for autofill.
+ * </ul>
+ *
+ * <p>Differently from the standard autofill workflow, on augmented autofill the service is
+ * responsible to generate the autofill UI and request the Android system to autofill the
+ * activity when the user taps an action in that UI (through the
+ * {@link FillController#autofill(List)} method).
+ *
+ * <p>The service <b>MUST</b> call {@link
+ * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible,
+ * passing {@code null} when it cannot fulfill the request.
+ *
+ * @param sessionId the session's id
+ * @param request the request to handle.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param controller object used to interact with the autofill system.
+ * @param callback object used to notify the result of the request. Service <b>must</b> call
+ * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}.
+ */
+ public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
+ @NonNull FillCallback callback) {
+ }
+
+ private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+ AutofillProxy proxy = null;
+ if (mAutofillProxies != null) {
+ proxy = mAutofillProxies.get(sessionId);
+ }
+ if (proxy == null) {
+ // TODO(b/111330312): this might be fine, in which case we should logv it
+ Log.w(TAG, "No proxy for session " + sessionId);
+ return;
+ }
+ proxy.destroy();
+ mAutofillProxies.remove(sessionId);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mAutofillProxies != null) {
+ final int size = mAutofillProxies.size();
+ pw.print("Number proxies: "); pw.println(size);
+ for (int i = 0; i < size; i++) {
+ final InteractionSessionId sessionId = mAutofillProxies.keyAt(i);
+ final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+ pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
+ proxy.dump(" ", pw);
+ }
+ }
+ }
+
/**
* Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a
* session.
@@ -142,4 +257,99 @@
* @param sessionId the id of the session to destroy
*/
public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+
+ /** @hide */
+ static final class AutofillProxy {
+ private final Object mLock = new Object();
+ private final IAugmentedAutofillManagerClient mClient;
+ private final int mAutofillSessionId;
+ public final InteractionSessionId sessionId;
+ public final AutofillId focusedId;
+
+ @GuardedBy("mLock")
+ private SystemPopupPresentationParams mSmartSuggestion;
+
+ @GuardedBy("mLock")
+ private FillWindow mFillWindow;
+
+ private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ this.sessionId = sessionId;
+ mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
+ mAutofillSessionId = autofillSessionId;
+ this.focusedId = focusedId;
+ // TODO(b/111330312): linkToDeath
+ }
+
+ @NonNull
+ public SystemPopupPresentationParams getSmartSuggestionParams() {
+ synchronized (mLock) {
+ if (mSmartSuggestion != null) {
+ return mSmartSuggestion;
+ }
+ Rect rect;
+ try {
+ rect = mClient.getViewCoordinates(focusedId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not get coordinates for " + focusedId);
+ return null;
+ }
+ if (rect == null) {
+ if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
+ return null;
+ }
+ mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+ return mSmartSuggestion;
+ }
+ }
+
+ public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
+ throws RemoteException {
+ final int size = pairs.size();
+ final List<AutofillId> ids = new ArrayList<>(size);
+ final List<AutofillValue> values = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
+ ids.add(pair.first);
+ values.add(pair.second);
+ }
+ mClient.autofill(mAutofillSessionId, ids, values);
+ }
+
+ public void setFillWindow(@NonNull FillWindow fillWindow) {
+ synchronized (mLock) {
+ mFillWindow = fillWindow;
+ }
+ }
+
+ public FillWindow getFillWindow() {
+ synchronized (mLock) {
+ return mFillWindow;
+ }
+ }
+
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId);
+ pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
+ pw.print(prefix); pw.print("client: "); pw.println(mClient);
+ final String prefix2 = prefix + " ";
+ if (mFillWindow != null) {
+ pw.print(prefix); pw.println("window:");
+ mFillWindow.dump(prefix2, pw);
+ }
+ if (mSmartSuggestion != null) {
+ pw.print(prefix); pw.println("smartSuggestion:");
+ mSmartSuggestion.dump(prefix2, pw);
+ }
+ }
+
+ private void destroy() {
+ synchronized (mLock) {
+ if (mFillWindow != null) {
+ if (DEBUG) Log.d(TAG, "destroying window");
+ mFillWindow.destroy();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java
new file mode 100644
index 0000000..c59069b
--- /dev/null
+++ b/core/java/android/service/intelligence/PresentationParams.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.DebugUtils;
+import android.view.View;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
+ * the intelligence service.
+ *
+ * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
+ *
+ * <ul>
+ * <li>A small area associated with suggestions (like a small strip in the top of the IME),
+ * returned by {@link #getSuggestionArea()}
+ * <li>The full area (like the full IME window), returned by {@link #getFullArea()}
+ * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
+ * </ul>
+ *
+ * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
+ * dimensions the smart suggestion window, so the service can use it to calculate the size of the
+ * view that will be passed to {@link FillWindow#update(Area, View, long)}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class PresentationParams {
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the top of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the left of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted in the right of its container.
+ */
+ public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted by the IME.
+ */
+ public static final int FLAG_HOST_IME = 0x10;
+
+ /**
+ * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
+ * window.
+ */
+ public static final int FLAG_HOST_SYSTEM = 0x20;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_HINT_GRAVITY_TOP,
+ FLAG_HINT_GRAVITY_BOTTOM,
+ FLAG_HINT_GRAVITY_LEFT,
+ FLAG_HINT_GRAVITY_RIGHT,
+ FLAG_HOST_IME,
+ FLAG_HOST_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flags {}
+
+
+ // /** @hide */
+ PresentationParams() {}
+
+ /**
+ * Gets the area of the suggestion strip for the given {@code metadata}
+ *
+ * @return strip dimensions, or {@code null} if the Smart Suggestion provider does not support
+ * suggestions strip.
+ */
+ @Nullable
+ public Area getSuggestionArea() {
+ return null;
+ }
+
+ /**
+ * Gets the full area for the of the Smart Suggestion provider.
+ *
+ * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
+ * embeding the UI on its full area.
+ */
+ @Nullable
+ public Area getFullArea() {
+ return null;
+ }
+
+ /**
+ * Gets flags associated with the Smart Suggestion.
+ *
+ * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
+ * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
+ * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
+ * {@link #FLAG_HOST_SYSTEM},
+ */
+ public @Flags int getFlags() {
+ return 0;
+ }
+
+ /** @hide */
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ final int flags = getFlags();
+ if (flags > 0) {
+ pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
+ }
+ }
+
+ private static String flagsToString(int flags) {
+ return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
+ }
+
+ /**
+ * Area associated with a {@link PresentationParams Smart Suggestions} provider.
+ *
+ * @hide
+ * */
+ @SystemApi
+ public abstract static class Area {
+
+ /** @hide */
+ public final AutofillProxy proxy;
+
+ private final Rect mBounds;
+
+ private Area(@NonNull AutofillProxy proxy, @NonNull Rect bounds) {
+ this.proxy = proxy;
+ mBounds = bounds;
+ }
+
+ /**
+ * Gets the area boundaries.
+ */
+ @NonNull
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ /**
+ * Gets a subarea limited by given boundaries.
+ *
+ * @param bounds boundaries relative to this Area.
+ *
+ * @throws {@link IllegalArgumentException} if the {@code bounds} is not fully-contained
+ * inside this full Area.
+ *
+ * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
+ * subaarea.
+ */
+ @Nullable
+ public Area getSubArea(@NonNull Rect bounds) {
+ // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return mBounds.toString();
+ }
+ }
+
+ /**
+ * System-provided poup window anchored to a view.
+ *
+ * <p>Used just for debugging purposes.
+ *
+ * @hide
+ */
+ public static final class SystemPopupPresentationParams extends PresentationParams {
+ private final Area mSuggestionArea;
+
+ public SystemPopupPresentationParams(@NonNull AutofillProxy proxy, @NonNull Rect rect) {
+ mSuggestionArea = new Area(proxy, rect) {};
+ }
+
+ @Override
+ public Area getSuggestionArea() {
+ return mSuggestionArea;
+ }
+
+ @Override
+ public int getFlags() {
+ return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
+ }
+
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+ pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
+ }
+ }
+}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d8bd002..ab94f43 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -47,4 +47,7 @@
void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
+ void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+ void onNotificationDirectReply(String key);
+ void onSuggestedReplySent(String key, in CharSequence reply, int source);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c1a3c2b..68da83f 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,6 +16,9 @@
package android.service.notification;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -33,6 +36,7 @@
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
import java.util.List;
/**
@@ -63,6 +67,13 @@
public abstract class NotificationAssistantService extends NotificationListenerService {
private static final String TAG = "NotificationAssistants";
+ /** @hide */
+ @Retention(SOURCE)
+ @IntDef({SOURCE_FROM_APP, SOURCE_FROM_ASSISTANT})
+ public @interface Source {}
+ public static final int SOURCE_FROM_APP = 0;
+ public static final int SOURCE_FROM_ASSISTANT = 1;
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -160,6 +171,29 @@
}
/**
+ * Implement this to know when a notification is expanded / collapsed.
+ * @param key the notification key
+ * @param isUserAction whether the expanded change is caused by user action.
+ * @param isExpanded whether the notification is expanded.
+ */
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {}
+
+ /**
+ * Implement this to know when a direct reply is sent from a notification.
+ * @param key the notification key
+ */
+ public void onNotificationDirectReply(String key) {}
+
+ /**
+ * Implement this to know when a suggested reply is sent.
+ * @param key the notification key
+ * @param reply the reply that is just sent
+ * @param source the source of the reply, e.g. SOURCE_FROM_APP
+ */
+ public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
+
+ /**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
@@ -255,12 +289,43 @@
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATIONS_SEEN,
args).sendToTarget();
}
+
+ @Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.argi1 = isUserAction ? 1 : 0;
+ args.argi2 = isExpanded ? 1 : 0;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_EXPANSION_CHANGED, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = reply;
+ args.argi2 = source;
+ mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget();
+ }
}
private final class MyHandler extends Handler {
public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
public static final int MSG_ON_NOTIFICATIONS_SEEN = 3;
+ public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
+ public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
+ public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -305,6 +370,31 @@
onNotificationsSeen(keys);
break;
}
+ case MSG_ON_NOTIFICATION_EXPANSION_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ boolean isUserAction = args.argi1 == 1;
+ boolean isExpanded = args.argi2 == 1;
+ args.recycle();
+ onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ break;
+ }
+ case MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ args.recycle();
+ onNotificationDirectReply(key);
+ break;
+ }
+ case MSG_ON_SUGGESTED_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ CharSequence reply = (CharSequence) args.arg2;
+ int source = args.argi2;
+ args.recycle();
+ onSuggestedReplySent(key, reply, source);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64eae0c..756a7c6 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,22 @@
}
@Override
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationChannelModification(String pkgName, UserHandle user,
NotificationChannel channel,
@ChannelOrGroupModificationTypes int modificationType) {
diff --git a/core/java/android/service/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
new file mode 100644
index 0000000..5fb7249
--- /dev/null
+++ b/core/java/android/service/sms/FinancialSmsService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.sms;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.database.CursorWindow;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * A service to support sms messages read for financial apps.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class FinancialSmsService extends Service {
+
+ private static final String TAG = "FinancialSmsService";
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest for the system to recognize it as a quota providing
+ * service.
+ */
+ public static final String ACTION_FINANCIAL_SERVICE_INTENT =
+ "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+
+ /** {@hide} **/
+ public static final String EXTRA_SMS_MSGS = "sms_messages";
+
+ private FinancialSmsServiceWrapper mWrapper;
+
+ private void getSmsMessages(RemoteCallback callback, Bundle params) {
+ final Bundle data = new Bundle();
+ CursorWindow smsMessages = onGetSmsMessages(params);
+ if (smsMessages != null) {
+ data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
+ }
+ callback.sendResult(data);
+ }
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+ /** @hide */
+ public FinancialSmsService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new FinancialSmsServiceWrapper();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Get sms messages for financial apps.
+ *
+ * @param params parameters passed in by the calling app.
+ * @return the {@code CursorWindow} with all sms messages for the app to read.
+ *
+ * {@hide}
+ */
+ @Nullable
+ @SystemApi
+ public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
+
+ private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
+ @Override
+ public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
+ mHandler.sendMessage(obtainMessage(
+ FinancialSmsService::getSmsMessages,
+ FinancialSmsService.this,
+ callback, params));
+ }
+ }
+
+}
diff --git a/core/java/android/service/sms/IFinancialSmsService.aidl b/core/java/android/service/sms/IFinancialSmsService.aidl
new file mode 100644
index 0000000..caabe58
--- /dev/null
+++ b/core/java/android/service/sms/IFinancialSmsService.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.sms;
+
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+/**
+ * Service used by financial apps to read sms messages.
+ *
+ * @hide
+ */
+oneway interface IFinancialSmsService
+{
+ void getSmsMessages(in RemoteCallback callback, in Bundle params);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6f51bec..f6bb762 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -129,7 +129,7 @@
Bundle extras;
boolean sync;
}
-
+
/**
* The actual implementation of a wallpaper. A wallpaper service may
* have multiple instances running (for example as a real wallpaper
@@ -144,7 +144,7 @@
HandlerCaller mCaller;
IWallpaperConnection mConnection;
IBinder mWindowToken;
-
+
boolean mInitializing = true;
boolean mVisible;
boolean mReportedVisible;
@@ -208,7 +208,6 @@
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
- DisplayManager mDisplayManager;
Display mDisplay;
private int mDisplayState;
@@ -419,7 +418,7 @@
public int getDesiredMinimumHeight() {
return mIWallpaperEngine.mReqHeight;
}
-
+
/**
* Return whether the wallpaper is currently visible to the user,
* this is the last value supplied to
@@ -1015,14 +1014,6 @@
return;
}
- mDisplayManager = getSystemService(DisplayManager.class);
- mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
- if (mDisplay == null) {
- // TODO(b/115486823) Ignore this engine.
- Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
- return;
- }
-
mIWallpaperEngine = wrapper;
mCaller = wrapper.mCaller;
mConnection = wrapper.mConnection;
@@ -1034,13 +1025,16 @@
mWindow.setSession(mSession);
mLayout.packageName = getPackageName();
- mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
+ mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
+ mCaller.getHandler());
+ mDisplay = mIWallpaperEngine.mDisplay;
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
onCreate(mSurfaceHolder);
-
+
mInitializing = false;
+
mReportedVisible = false;
updateSurface(false, false, false);
}
@@ -1202,8 +1196,8 @@
mDestroyed = true;
- if (mDisplayManager != null) {
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ if (mIWallpaperEngine.mDisplayManager != null) {
+ mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
}
if (mVisible) {
@@ -1272,7 +1266,9 @@
int mReqWidth;
int mReqHeight;
final Rect mDisplayPadding = new Rect();
- int mDisplayId;
+ final int mDisplayId;
+ final DisplayManager mDisplayManager;
+ final Display mDisplay;
Engine mEngine;
@@ -1289,7 +1285,15 @@
mReqHeight = reqHeight;
mDisplayPadding.set(padding);
mDisplayId = displayId;
-
+
+ // Create a display context before onCreateEngine.
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplay = mDisplayManager.getDisplay(mDisplayId);
+
+ if (mDisplay == null) {
+ // Ignore this engine.
+ throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
+ }
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index b970c25..85b6b88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -46,6 +46,7 @@
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.LineBackgroundSpan;
+import android.text.style.LineHeightSpan;
import android.text.style.LocaleSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
@@ -733,7 +734,9 @@
/** @hide */
public static final int LINE_BACKGROUND_SPAN = 27;
/** @hide */
- public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
+ public static final int LINE_HEIGHT_SPAN = 28;
+ /** @hide */
+ public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -928,6 +931,10 @@
readSpan(p, sp, new LineBackgroundSpan.Standard(p));
break;
+ case LINE_HEIGHT_SPAN:
+ readSpan(p, sp, new LineHeightSpan.Standard(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 2742ae0..a5d5af2 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -20,7 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Px;
import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
import android.text.TextPaint;
+import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -71,7 +74,7 @@
* covers only part of the paragraph.
* </p>
*/
- class Standard implements LineHeightSpan {
+ class Standard implements LineHeightSpan, ParcelableSpan {
private final @Px int mHeight;
/**
@@ -82,6 +85,48 @@
mHeight = height;
}
+ /**
+ * Constructor called from {@link TextUtils} to restore the span from a parcel
+ */
+ public Standard(Parcel src) {
+ mHeight = src.readInt();
+ }
+
+ /**
+ * Returns the line height specified by this span.
+ */
+ @Px
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.LINE_HEIGHT_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mHeight);
+ }
+
@Override
public void chooseHeight(@NonNull CharSequence text, int start, int end,
int spanstartv, int lineHeight,
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f4dad62..eef7ea2 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -29,6 +29,7 @@
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.URLSpan;
+import android.util.Log;
import android.util.Patterns;
import android.webkit.WebView;
import android.widget.TextView;
@@ -72,6 +73,9 @@
*/
public class Linkify {
+
+ private static final String LOG_TAG = "Linkify";
+
/**
* Bit field indicating that web URLs should be matched in methods that
* take an options mask
@@ -310,6 +314,11 @@
*/
private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
@Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+ if (text != null && containsUnsupportedCharacters(text.toString())) {
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return false;
+ }
+
if (mask == 0) {
return false;
}
@@ -356,6 +365,29 @@
}
/**
+ * Returns true if the specified text contains at least one unsupported character for applying
+ * links. Also logs the error.
+ *
+ * @param text the text to apply links to
+ * @hide
+ */
+ public static boolean containsUnsupportedCharacters(String text) {
+ if (text.contains("\u202C")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202C");
+ return true;
+ }
+ if (text.contains("\u202D")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202D");
+ return true;
+ }
+ if (text.contains("\u202E")) {
+ Log.e(LOG_TAG, "Unsupported character for applying links: u202E");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Scans the text of the provided TextView and turns all occurrences of
* the link types indicated in the mask into clickable links. If matches
* are found the movement method for the TextView is set to
@@ -560,6 +592,11 @@
@Nullable String defaultScheme, @Nullable String[] schemes,
@Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
@Nullable UrlSpanFactory urlSpanFactory) {
+ if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return false;
+ }
+
final String[] schemesCopy;
if (defaultScheme == null) defaultScheme = "";
if (schemes == null || schemes.length < 1) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f19d40a..0eaef5a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3373,9 +3373,8 @@
*
* |-------|-------|-------|-------|
* 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED
- * 1 PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
* |-------|-------|-------|-------|
*/
@@ -3396,28 +3395,14 @@
* Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
* should be called.
*
- * The idea is to call notifyNodeAdded() after the view is layout and visible, then call
- * notifyNodeRemoved() when it's gone (without known when it was removed from the parent).
- *
- * TODO(b/111276913): the current algortighm could probably be optimized and some of them
- * removed
+ * The idea is to call notifyAppeared() after the view is layout and visible, then call
+ * notifyDisappeared() when it's gone (without known when it was removed from the parent).
*/
- private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT = 0x10;
- private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED = 0x20;
- private static final int PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE = 0x40;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
/* End of masks for mPrivateFlags4 */
- private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED = 1;
- private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED = 0;
-
- @IntDef(flag = true, prefix = { "CONTENT_CAPTURE_NOTIFICATION_TYPE_" }, value = {
- CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED,
- CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED
- })
- @Retention(RetentionPolicy.SOURCE)
- private @interface ContentCaptureNotificationType {}
-
/** @hide */
protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
/** @hide */
@@ -8152,6 +8137,9 @@
* the user, and the activity rendering the view is enabled for Content Capture) is laid out and
* is visible.
*
+ * <p>The populated structure is then passed to the service through
+ * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}.
+ *
* <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
* <ul>
* <li>{@link ViewStructure#setChildCount(int)}
@@ -8165,16 +8153,9 @@
* <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
* <li>{@link ViewStructure#setDataIsSensitive(boolean)}
* </ul>
- *
- * @return whether the IntelligenceService should be notified that the view was added (through
- * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view
- * hierarchy. Most views should return {@code true} here, but views that contains virtual
- * hierarchy might opt to return {@code false} and notify the manager independently, as the
- * virtual views are rendered.
*/
- public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+ public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
- return true;
}
/** @hide */
@@ -8937,65 +8918,61 @@
* Helper used to notify the {@link IntelligenceManager} when the view is removed or
* added, based on whether it's laid out and visible, and without knowing if the parent removed
* it from the view hierarchy.
+ *
+ * <p>This method is called from many places (visibility changed, view laid out, view attached
+ * or detached to/from window, etc...) and hence must contain the logic to call the manager, as
+ * described below:
+ *
+ * <ol>
+ * <li>It should only be called when content capture is enabled for the view.
+ * <li>It must call viewAppeared() before viewDisappeared()
+ * <li>viewAppearead() can only be called when the view is visible and laidout
+ * <li>It should not call the same event twice.
+ * </ol>
*/
- // TODO(b/111276913): make sure the current algorithm covers all cases. For example, it should
- // probably be called every time notifyEnterOrExitForAutoFillIfNeeded() is called as well.
- private void notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- @ContentCaptureNotificationType int type) {
- if (type != CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
- && type != CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
- // Sanity check so it does not screw up the flags
- Log.wtf(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded(): "
- + "invalid type " + type + " for " + this);
- return;
- }
-
- if (!isImportantForContentCapture()) return;
+ private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
if (im == null || !im.isContentCaptureEnabled()) return;
- // Make sure event is notified just once, and reset the
- // PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE flag
- boolean ignoreNotification = false;
- if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
- == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
- ignoreNotification = true;
- } else {
- mPrivateFlags4 |= PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
- }
- } else {
- if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
- == CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
- ignoreNotification = true;
- } else {
- mPrivateFlags4 &= ~PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
- }
- }
- if (ignoreNotification) {
- if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
- // TODO(b/111276913): remove this log statement if the algorithm is not improved
- // (right now it's called too many times when the activity is stopped and/or views
- // disappear
- Log.v(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded("
- + type + "): ignoring repeated notification on " + this);
- }
- return;
- }
+ // NOTE: isImportantForContentCapture() is more expensive than im.isContentCaptureEnabled()
+ if (!isImportantForContentCapture()) return;
- if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+ if (appeared) {
+ if (!isLaidOut() || !isVisibleToUser()
+ || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+ + isLaidOut() + ", visible=" + isVisibleToUser()
+ + ": alreadyNotifiedAppeared="
+ + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0));
+ }
+ return;
+ }
+ // All good: notify the manager...
final ViewStructure structure = im.newViewStructure(this);
- boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
- if (notifyMgr) {
- im.notifyViewAppeared(structure);
- }
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
+ onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ im.notifyViewAppeared(structure);
+ // ...and set the flags
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
} else {
- if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
- return; // skip initial notification
+ if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
+ || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this
+ + ": notifiedAppeared="
+ + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+ + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+ & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+ }
+ return;
}
+ // All good: notify the manager...
im.notifyViewDisappeared(getAutofillId());
+ // ...and set the flags
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
}
}
@@ -12902,6 +12879,7 @@
public void dispatchStartTemporaryDetach() {
mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
notifyEnterOrExitForAutoFillIfNeeded(false);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
onStartTemporaryDetach();
}
@@ -12928,6 +12906,7 @@
notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
/**
@@ -13509,9 +13488,8 @@
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
}
}
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(isVisible
- ? CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
- : CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
}
/**
@@ -19082,6 +19060,7 @@
needGlobalAttributesUpdate(false);
notifyEnterOrExitForAutoFillIfNeeded(true);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
@UnsupportedAppUsage
@@ -19131,8 +19110,7 @@
}
notifyEnterOrExitForAutoFillIfNeeded(false);
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
}
/**
@@ -21438,12 +21416,7 @@
notifyEnterOrExitForAutoFillIfNeeded(true);
}
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
- && (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT) == 0) {
- notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
- CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED);
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT;
- }
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
}
private boolean hasParentWantsFocus() {
@@ -23157,26 +23130,24 @@
* Modifies the input matrix such that it maps view-local coordinates to
* on-screen coordinates.
*
- * @param m input matrix to modify
- * @hide
+ * @param matrix input matrix to modify
*/
- @UnsupportedAppUsage
- public void transformMatrixToGlobal(Matrix m) {
+ public void transformMatrixToGlobal(Matrix matrix) {
final ViewParent parent = mParent;
if (parent instanceof View) {
final View vp = (View) parent;
- vp.transformMatrixToGlobal(m);
- m.preTranslate(-vp.mScrollX, -vp.mScrollY);
+ vp.transformMatrixToGlobal(matrix);
+ matrix.preTranslate(-vp.mScrollX, -vp.mScrollY);
} else if (parent instanceof ViewRootImpl) {
final ViewRootImpl vr = (ViewRootImpl) parent;
- vr.transformMatrixToGlobal(m);
- m.preTranslate(0, -vr.mCurScrollY);
+ vr.transformMatrixToGlobal(matrix);
+ matrix.preTranslate(0, -vr.mCurScrollY);
}
- m.preTranslate(mLeft, mTop);
+ matrix.preTranslate(mLeft, mTop);
if (!hasIdentityMatrix()) {
- m.preConcat(getMatrix());
+ matrix.preConcat(getMatrix());
}
}
@@ -23184,26 +23155,24 @@
* Modifies the input matrix such that it maps on-screen coordinates to
* view-local coordinates.
*
- * @param m input matrix to modify
- * @hide
+ * @param matrix input matrix to modify
*/
- @UnsupportedAppUsage
- public void transformMatrixToLocal(Matrix m) {
+ public void transformMatrixToLocal(Matrix matrix) {
final ViewParent parent = mParent;
if (parent instanceof View) {
final View vp = (View) parent;
- vp.transformMatrixToLocal(m);
- m.postTranslate(vp.mScrollX, vp.mScrollY);
+ vp.transformMatrixToLocal(matrix);
+ matrix.postTranslate(vp.mScrollX, vp.mScrollY);
} else if (parent instanceof ViewRootImpl) {
final ViewRootImpl vr = (ViewRootImpl) parent;
- vr.transformMatrixToLocal(m);
- m.postTranslate(0, vr.mCurScrollY);
+ vr.transformMatrixToLocal(matrix);
+ matrix.postTranslate(0, vr.mCurScrollY);
}
- m.postTranslate(-mLeft, -mTop);
+ matrix.postTranslate(-mLeft, -mTop);
if (!hasIdentityMatrix()) {
- m.postConcat(getInverseMatrix());
+ matrix.postConcat(getInverseMatrix());
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d4c7069..9227249 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -202,6 +202,14 @@
public static final String EXTRA_RESTORE_SESSION_TOKEN =
"android.view.autofill.extra.RESTORE_SESSION_TOKEN";
+ /**
+ * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
+ "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
+
private static final String SESSION_ID_TAG = "android:sessionId";
private static final String STATE_TAG = "android:state";
private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -370,6 +378,9 @@
private Cleaner mServiceClientCleaner;
@GuardedBy("mLock")
+ private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
+
+ @GuardedBy("mLock")
private AutofillCallback mCallback;
private final Context mContext;
@@ -1664,6 +1675,8 @@
final IAutoFillManager service = mService;
final IAutoFillManagerClient serviceClient = mServiceClient;
mServiceClientCleaner = Cleaner.create(this, () -> {
+ // TODO(b/111330312): call service to also remove reference to
+ // mAugmentedAutofillServiceClient
try {
service.removeClient(serviceClient, userId);
} catch (RemoteException e) {
@@ -1808,6 +1821,7 @@
if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
mServiceClient = null;
+ mAugmentedAutofillServiceClient = null;
if (mServiceClientCleaner != null) {
mServiceClientCleaner.clean();
mServiceClientCleaner = null;
@@ -2054,6 +2068,29 @@
}
}
+ /**
+ * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
+ *
+ * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
+ * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
+ */
+ private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillServiceClient == null) {
+ mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
+ }
+ final Bundle resultData = new Bundle();
+ resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
+ mAugmentedAutofillServiceClient.asBinder());
+
+ try {
+ result.send(0, resultData);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
+ }
+ }
+ }
+
/** @hide */
public void requestHideFillUi() {
requestHideFillUi(mIdShownFillUi, true);
@@ -2801,7 +2838,7 @@
private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
private final WeakReference<AutofillManager> mAfm;
- AutofillManagerClient(AutofillManager autofillManager) {
+ private AutofillManagerClient(AutofillManager autofillManager) {
mAfm = new WeakReference<>(autofillManager);
}
@@ -2904,6 +2941,50 @@
afm.post(() -> afm.setSessionFinished(newState));
}
}
+
+ @Override
+ public void getAugmentedAutofillClient(IResultReceiver result) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.getAugmentedAutofillClient(result));
+ }
+ }
+ }
+
+ private static final class AugmentedAutofillManagerClient
+ extends IAugmentedAutofillManagerClient.Stub {
+ private final WeakReference<AutofillManager> mAfm;
+
+ private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
+ mAfm = new WeakReference<>(autofillManager);
+ }
+
+ @Override
+ public Rect getViewCoordinates(@NonNull AutofillId id) {
+ // TODO(b/111330312): use handler / callback?
+ final AutofillManager afm = mAfm.get();
+ if (afm == null) return null;
+
+ final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
+ // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
+ // fix (for example, take system status bar height into account) logic below
+ final int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
+ location[1] + view.getHeight());
+ if (sVerbose) {
+ Log.v(TAG, "Coordinates for " + id + ": " + rect);
+ }
+ return rect;
+ }
+
+ @Override
+ public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.autofill(sessionId, ids, values));
+ }
+ }
}
/**
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
new file mode 100644
index 0000000..67cd0bf
--- /dev/null
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import java.util.List;
+
+import android.graphics.Rect;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Object running in the application process and responsible to provide the functionalities
+ * required by an Augmented Autofill service.
+ *
+ * @hide
+ */
+interface IAugmentedAutofillManagerClient {
+ Rect getViewCoordinates(in AutofillId id);
+ void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 0ff7a0b..63394b4 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -27,6 +27,8 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.KeyEvent;
+import com.android.internal.os.IResultReceiver;
+
/**
* Object running in the application process and responsible for autofilling it.
*
@@ -93,8 +95,18 @@
/**
* Marks the state of the session as finished.
+ *
* @param newState STATE_FINISHED (because the autofill service returned a null
* FillResponse) or STATE_UNKNOWN (because the session was removed).
*/
void setSessionFinished(int newState);
+
+ /**
+ * Gets a reference to the binder object that can be used by the Augmented Autofill service.
+ *
+ * @param receiver, whose AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT extra will contain
+ * the reference.
+ */
+ void getAugmentedAutofillClient(in IResultReceiver result);
+
}
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index dfa52d9..755c54c 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -391,7 +391,7 @@
}
/**
- * Called by apps to explicitly enabled or disable content capture.
+ * Called by apps to explicitly enable or disable content capture.
*
* <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
* it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
index cc78e6b..ea57461 100644
--- a/core/java/android/view/intelligence/ViewNode.java
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -238,6 +238,8 @@
@Override
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mText = text;
// TODO(b/111276913): implement or move to superclass
}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
new file mode 100644
index 0000000..8df83c0
--- /dev/null
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.app.Person;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class for action suggestions.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class ActionsSuggestionsHelper {
+ private static final int USER_LOCAL = 0;
+ private static final int FIRST_NON_LOCAL_USER = 1;
+
+ private ActionsSuggestionsHelper() {}
+
+ /**
+ * Converts the messages to a list of native messages object that the model can understand.
+ * <p>
+ * User id encoding - local user is represented as 0, Other users are numbered according to
+ * how far before they spoke last time in the conversation. For example, considering this
+ * conversation:
+ * <ul>
+ * <li> User A: xxx
+ * <li> Local user: yyy
+ * <li> User B: zzz
+ * </ul>
+ * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
+ */
+ @NonNull
+ public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
+ @NonNull List<ConversationActions.Message> messages) {
+ List<ConversationActions.Message> messagesWithText =
+ messages.stream()
+ .filter(message -> !TextUtils.isEmpty(message.getText()))
+ .collect(Collectors.toCollection(ArrayList::new));
+ if (messagesWithText.isEmpty()) {
+ return new ActionsSuggestionsModel.ConversationMessage[0];
+ }
+ int size = messagesWithText.size();
+ // If the last message (the most important one) does not have the Person object, we will
+ // just use the last message and consider this message is sent from a remote user.
+ ConversationActions.Message lastMessage = messages.get(size - 1);
+ boolean useLastMessageOnly = lastMessage.getAuthor() == null;
+ if (useLastMessageOnly) {
+ return new ActionsSuggestionsModel.ConversationMessage[]{
+ new ActionsSuggestionsModel.ConversationMessage(
+ FIRST_NON_LOCAL_USER,
+ lastMessage.getText().toString())};
+ }
+
+ // Encode the messages in the reverse order, stop whenever the Person object is missing.
+ Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
+ PersonEncoder personEncoder = new PersonEncoder();
+ for (int i = size - 1; i >= 0; i--) {
+ ConversationActions.Message message = messagesWithText.get(i);
+ if (message.getAuthor() == null) {
+ break;
+ }
+ nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
+ personEncoder.encode(message.getAuthor()),
+ message.getText().toString()));
+ }
+ return nativeMessages.toArray(
+ new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
+ }
+
+ private static final class PersonEncoder {
+ private final Map<Person, Integer> mMapping = new ArrayMap<>();
+ private int mNextUserId = FIRST_NON_LOCAL_USER;
+
+ private int encode(Person person) {
+ if (ConversationActions.Message.PERSON_USER_LOCAL.equals(person)) {
+ return USER_LOCAL;
+ }
+ Integer result = mMapping.get(person);
+ if (result == null) {
+ mMapping.put(person, mNextUserId);
+ result = mNextUserId;
+ mNextUserId++;
+ }
+ return result;
+ }
+ }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 5fcf227..1a7b911 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -345,6 +345,16 @@
/** Represents a message in the conversation. */
public static final class Message implements Parcelable {
+ /**
+ * Represents the local user.
+ *
+ * @see Builder#setAuthor(Person)
+ */
+ public static final Person PERSON_USER_LOCAL =
+ new Person.Builder()
+ .setKey("text-classifier-conversation-actions-local-user")
+ .build();
+
@Nullable
private final Person mAuthor;
@Nullable
@@ -446,7 +456,11 @@
@Nullable
private Bundle mExtras;
- /** Sets the person who composed this message. */
+ /**
+ * Sets the person who composed this message.
+ * <p>
+ * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+ */
@NonNull
public Builder setAuthor(@Nullable Person author) {
mAuthor = author;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f6c3d77..e0910c0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -105,7 +105,7 @@
/**
* @hide
*/
- static final TextClassification EMPTY = new TextClassification.Builder().build();
+ public static final TextClassification EMPTY = new TextClassification.Builder().build();
private static final String LOG_TAG = "TextClassification";
// TODO(toki): investigate a way to derive this based on device properties.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 524f709..a2536cb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -90,6 +90,11 @@
String TYPE_DATE_TIME = "datetime";
/** Flight number in IATA format. */
String TYPE_FLIGHT_NUMBER = "flight";
+ /**
+ * Word that users may be interested to look up for meaning.
+ * @hide
+ */
+ String TYPE_DICTIONARY = "dictionary";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -103,6 +108,7 @@
TYPE_DATE,
TYPE_DATE_TIME,
TYPE_FLIGHT_NUMBER,
+ TYPE_DICTIONARY
})
@interface EntityType {}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..8e14dfd 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,9 +40,9 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -269,17 +269,17 @@
final ZonedDateTime refTime = ZonedDateTime.now();
final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
? request.getEntityConfig().resolveEntityListModifications(
- getEntitiesForHints(request.getEntityConfig().getHints()))
+ getEntitiesForHints(request.getEntityConfig().getHints()))
: mSettings.getEntityListDefault();
final AnnotatorModel annotatorImpl =
getAnnotatorImpl(request.getDefaultLocales());
final AnnotatorModel.AnnotatedSpan[] annotations =
annotatorImpl.annotate(
- textString,
- new AnnotatorModel.AnnotationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- concatenateLocales(request.getDefaultLocales())));
+ textString,
+ new AnnotatorModel.AnnotationOptions(
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
+ concatenateLocales(request.getDefaultLocales())));
for (AnnotatorModel.AnnotatedSpan span : annotations) {
final AnnotatorModel.ClassificationResult[] results =
span.getClassification();
@@ -373,20 +373,13 @@
// Actions model is optional, fallback if it is not available.
return mFallback.suggestConversationActions(request);
}
- List<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayList<>();
- for (ConversationActions.Message message : request.getConversation()) {
- if (TextUtils.isEmpty(message.getText())) {
- continue;
- }
- // TODO: We need to map the Person object to user id.
- int userId = 1;
- nativeMessages.add(
- new ActionsSuggestionsModel.ConversationMessage(
- userId, message.getText().toString()));
+ ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
+ ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+ if (nativeMessages.length == 0) {
+ return mFallback.suggestConversationActions(request);
}
ActionsSuggestionsModel.Conversation nativeConversation =
- new ActionsSuggestionsModel.Conversation(nativeMessages.toArray(
- new ActionsSuggestionsModel.ConversationMessage[0]));
+ new ActionsSuggestionsModel.Conversation(nativeMessages);
ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
actionsImpl.suggestActions(nativeConversation, null);
@@ -523,10 +516,10 @@
final TextClassification.Builder builder = new TextClassification.Builder()
.setText(classifiedText);
- final int size = classifications.length;
+ final int typeCount = classifications.length;
AnnotatorModel.ClassificationResult highestScoringResult =
- size > 0 ? classifications[0] : null;
- for (int i = 0; i < size; i++) {
+ typeCount > 0 ? classifications[0] : null;
+ for (int i = 0; i < typeCount; i++) {
builder.setEntityType(classifications[i].getCollection(),
classifications[i].getScore());
if (classifications[i].getScore() > highestScoringResult.getScore()) {
@@ -534,9 +527,12 @@
}
}
+ // TODO: Make this configurable.
+ final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
boolean isPrimaryAction = true;
for (LabeledIntent labeledIntent : IntentFactory.create(
- mContext, classifiedText, referenceTime, highestScoringResult)) {
+ mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold),
+ referenceTime, highestScoringResult)) {
final RemoteAction action = labeledIntent.asRemoteAction(mContext);
if (action == null) {
continue;
@@ -558,6 +554,42 @@
return builder.setId(createId(text, start, end)).build();
}
+ private boolean isForeignText(String text, float threshold) {
+ // TODO: Revisit this algorithm.
+ try {
+ final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
+ if (langResults.length <= 0) {
+ return false;
+ }
+
+ LangIdModel.LanguageResult highestScoringResult = langResults[0];
+ for (int i = 1; i < langResults.length; i++) {
+ if (langResults[i].getScore() > highestScoringResult.getScore()) {
+ highestScoringResult = langResults[i];
+ }
+ }
+ if (highestScoringResult.getScore() < threshold) {
+ return false;
+ }
+ // TODO: Remove
+ Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
+ highestScoringResult.getLanguage(), highestScoringResult.getScore()));
+
+ final Locale detected = new Locale(highestScoringResult.getLanguage());
+ final LocaleList deviceLocales = LocaleList.getDefault();
+ final int size = deviceLocales.size();
+ for (int i = 0; i < size; i++) {
+ if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
+ return false;
+ }
+ }
+ return true;
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t);
+ }
+ return false;
+ }
+
@Override
public void dump(@NonNull IndentingPrintWriter printWriter) {
synchronized (mLock) {
@@ -608,7 +640,8 @@
/**
* Helper class to store the information from which RemoteActions are built.
*/
- private static final class LabeledIntent {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static final class LabeledIntent {
static final int DEFAULT_REQUEST_CODE = 0;
@@ -643,7 +676,8 @@
return mDescription;
}
- Intent getIntent() {
+ @VisibleForTesting
+ public Intent getIntent() {
return mIntent;
}
@@ -687,7 +721,8 @@
/**
* Creates intents based on the classification type.
*/
- static final class IntentFactory {
+ @VisibleForTesting
+ public static final class IntentFactory {
private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
@@ -698,53 +733,70 @@
public static List<LabeledIntent> create(
Context context,
String text,
+ boolean foreignText,
@Nullable Instant referenceTime,
@Nullable AnnotatorModel.ClassificationResult classification) {
final String type = classification != null
? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
- : null;
+ : "";
text = text.trim();
+ final List<LabeledIntent> actions;
switch (type) {
case TextClassifier.TYPE_EMAIL:
- return createForEmail(context, text);
+ actions = createForEmail(context, text);
+ break;
case TextClassifier.TYPE_PHONE:
- return createForPhone(context, text);
+ actions = createForPhone(context, text);
+ break;
case TextClassifier.TYPE_ADDRESS:
- return createForAddress(context, text);
+ actions = createForAddress(context, text);
+ break;
case TextClassifier.TYPE_URL:
- return createForUrl(context, text);
- case TextClassifier.TYPE_DATE:
+ actions = createForUrl(context, text);
+ break;
+ case TextClassifier.TYPE_DATE: // fall through
case TextClassifier.TYPE_DATE_TIME:
if (classification.getDatetimeResult() != null) {
final Instant parsedTime = Instant.ofEpochMilli(
classification.getDatetimeResult().getTimeMsUtc());
- return createForDatetime(context, type, referenceTime, parsedTime);
+ actions = createForDatetime(context, type, referenceTime, parsedTime);
} else {
- return new ArrayList<>();
+ actions = new ArrayList<>();
}
+ break;
case TextClassifier.TYPE_FLIGHT_NUMBER:
- return createForFlight(context, text);
+ actions = createForFlight(context, text);
+ break;
+ case TextClassifier.TYPE_DICTIONARY:
+ actions = createForDictionary(context, text);
+ break;
default:
- return new ArrayList<>();
+ actions = new ArrayList<>();
+ break;
}
+ if (foreignText) {
+ insertTranslateAction(actions, context, text);
+ }
+ return actions;
}
@NonNull
private static List<LabeledIntent> createForEmail(Context context, String text) {
- return Arrays.asList(
- new LabeledIntent(
- context.getString(com.android.internal.R.string.email),
- context.getString(com.android.internal.R.string.email_desc),
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE),
- new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- context.getString(com.android.internal.R.string.add_contact_desc),
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
- text.hashCode()));
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.email),
+ context.getString(com.android.internal.R.string.email_desc),
+ new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("mailto:%s", text))),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.add_contact),
+ context.getString(com.android.internal.R.string.add_contact_desc),
+ new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+ text.hashCode()));
+ return actions;
}
@NonNull
@@ -801,12 +853,14 @@
if (Uri.parse(text).getScheme() == null) {
text = "http://" + text;
}
- return Arrays.asList(new LabeledIntent(
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.browse),
context.getString(com.android.internal.R.string.browse_desc),
new Intent(Intent.ACTION_VIEW, Uri.parse(text))
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
LabeledIntent.DEFAULT_REQUEST_CODE));
+ return actions;
}
@NonNull
@@ -828,12 +882,14 @@
@NonNull
private static List<LabeledIntent> createForFlight(Context context, String text) {
- return Arrays.asList(new LabeledIntent(
+ final List<LabeledIntent> actions = new ArrayList<>();
+ actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.view_flight),
context.getString(com.android.internal.R.string.view_flight_desc),
new Intent(Intent.ACTION_WEB_SEARCH)
.putExtra(SearchManager.QUERY, text),
text.hashCode()));
+ return actions;
}
@NonNull
@@ -864,5 +920,27 @@
parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
parsedTime.hashCode());
}
+
+ private static void insertTranslateAction(
+ List<LabeledIntent> actions, Context context, String text) {
+ actions.add(new LabeledIntent(
+ context.getString(com.android.internal.R.string.translate),
+ context.getString(com.android.internal.R.string.translate_desc),
+ new Intent(Intent.ACTION_TRANSLATE)
+ // TODO: Probably better to introduce a "translate" scheme instead of
+ // using EXTRA_TEXT.
+ .putExtra(Intent.EXTRA_TEXT, text),
+ text.hashCode()));
+ }
+
+ @NonNull
+ private static List<LabeledIntent> createForDictionary(Context context, String text) {
+ return Arrays.asList(new LabeledIntent(
+ context.getString(com.android.internal.R.string.define),
+ context.getString(com.android.internal.R.string.define_desc),
+ new Intent(Intent.ACTION_DEFINE)
+ .putExtra(Intent.EXTRA_TEXT, text),
+ text.hashCode()));
+ }
}
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 02aee50..1e42c41 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -59,7 +59,7 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
- STATUS_DIFFERENT_TEXT})
+ STATUS_DIFFERENT_TEXT, STATUS_UNSUPPORTED_CHARACTER})
public @interface Status {}
/** Links were successfully applied to the text. */
@@ -74,6 +74,9 @@
/** The specified text does not match the text used to generate the links. */
public static final int STATUS_DIFFERENT_TEXT = 3;
+ /** The specified text contains unsupported characters. */
+ public static final int STATUS_UNSUPPORTED_CHARACTER = 4;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index be4c3bc..8af4233 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -107,6 +107,13 @@
Preconditions.checkNotNull(textLinks);
final String textString = text.toString();
+
+ if (Linkify.containsUnsupportedCharacters(textString)) {
+ // Do not apply links to text containing unsupported characters.
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
+ }
+
if (!textString.startsWith(textLinks.getText())) {
return TextLinks.STATUS_DIFFERENT_TEXT;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1093719..414cb8f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -410,6 +410,9 @@
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
+ if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+ setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+ }
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
@@ -2695,8 +2698,8 @@
}
@Override
- public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
- return mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
+ public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+ mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
}
@Override
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index ceada07..95e7a986 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -341,10 +341,9 @@
return true; // true is the default value returned by View.isVisibleToUserForAutofill()
}
- default boolean onProvideContentCaptureStructure(
+ default void onProvideContentCaptureStructure(
@SuppressWarnings("unused") android.view.ViewStructure structure,
@SuppressWarnings("unused") int flags) {
- return false; // WebView provides virtual views and is responsible to notify manager
}
public AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 12cc54d..c21182c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -106,9 +106,9 @@
private boolean mHaveFrame = false;
@UnsupportedAppUsage
private boolean mAdjustViewBounds = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMaxWidth = Integer.MAX_VALUE;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mMaxHeight = Integer.MAX_VALUE;
// these are applied to the drawable
@@ -1331,9 +1331,17 @@
}
}
- /** @hide */
- @UnsupportedAppUsage
- public void animateTransform(Matrix matrix) {
+ /**
+ * Applies a temporary transformation {@link Matrix} to the view's drawable when it is drawn.
+ * Allows custom scaling, translation, and perspective distortion during an animation.
+ *
+ * This method is a lightweight analogue of {@link ImageView#setImageMatrix(Matrix)} to use
+ * only during animations as this matrix will be cleared after the next drawable
+ * update or view's bounds change.
+ *
+ * @param matrix The transformation parameters in matrix form.
+ */
+ public void animateTransform(@Nullable Matrix matrix) {
if (mDrawable == null) {
return;
}
@@ -1341,6 +1349,7 @@
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
mDrawable.setBounds(0, 0, vwidth, vheight);
+ mDrawMatrix = null;
} else {
mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
if (mDrawMatrix == null) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6cb0eaa..4caf288 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -31,6 +31,7 @@
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
+import android.text.util.Linkify;
import android.util.Log;
import android.view.ActionMode;
import android.view.textclassifier.SelectionEvent;
@@ -687,17 +688,6 @@
mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
}
- @TextClassifier.WidgetType
- private static String getWidetType(TextView textView) {
- if (textView.isTextEditable()) {
- return TextClassifier.WIDGET_TYPE_EDITTEXT;
- }
- if (textView.isTextSelectable()) {
- return TextClassifier.WIDGET_TYPE_TEXTVIEW;
- }
- return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
- }
-
public void logSelectionStarted(
TextClassifier classificationSession,
CharSequence text, int index,
@@ -1045,7 +1035,12 @@
trimText();
final TextClassification classification;
- if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+ if (Linkify.containsUnsupportedCharacters(mText)) {
+ // Do not show smart actions for text containing unsupported characters.
+ android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+ classification = TextClassification.EMPTY;
+ } else if (mContext.getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.P) {
final TextClassification.Request request =
new TextClassification.Request.Builder(
mTrimmedText, mRelativeStart, mRelativeEnd)
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index d55c09f..79dc670 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1419,27 +1419,10 @@
return Switch.class.getName();
}
+ /** @hide */
@Override
- public void onProvideStructure(ViewStructure structure) {
- super.onProvideStructure(structure);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- }
-
- @Override
- public void onProvideAutofillStructure(ViewStructure structure, int flags) {
- super.onProvideAutofillStructure(structure, flags);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- }
-
- @Override
- public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
- final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
- onProvideStructureForAssistOrAutofillOrViewCapture(structure);
- return notifyManager;
- }
-
- // NOTE: currently there is no difference for any type, so it doesn't take flags
- private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) {
+ protected void onProvideStructure(@NonNull ViewStructure structure,
+ @ViewStructureType int viewFor, int flags) {
CharSequence switchText = isChecked() ? mTextOn : mTextOff;
if (!TextUtils.isEmpty(switchText)) {
CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35be766..4ed9924f0d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10942,6 +10942,9 @@
if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
if (mLayout == null) {
+ if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
+ Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
+ }
assumeLayout();
}
Layout layout = mLayout;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e2b8f7d..5d08a25 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -309,9 +309,6 @@
} else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
- } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- prefer = RECOMMEND_INSTALL_EXTERNAL;
- checkBoth = false;
} else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
diff --git a/core/java/com/android/internal/os/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
index 22435ae..b3aec0c 100644
--- a/core/java/com/android/internal/os/KernelCpuProcStringReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -59,6 +60,7 @@
private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state";
private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time";
private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time";
+ private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat";
private static final KernelCpuProcStringReader FREQ_TIME_READER =
new KernelCpuProcStringReader(PROC_UID_FREQ_TIME);
@@ -66,19 +68,25 @@
new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME);
private static final KernelCpuProcStringReader CLUSTER_TIME_READER =
new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME);
+ private static final KernelCpuProcStringReader USER_SYS_TIME_READER =
+ new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME);
- public static KernelCpuProcStringReader getFreqTimeReaderInstance() {
+ static KernelCpuProcStringReader getFreqTimeReaderInstance() {
return FREQ_TIME_READER;
}
- public static KernelCpuProcStringReader getActiveTimeReaderInstance() {
+ static KernelCpuProcStringReader getActiveTimeReaderInstance() {
return ACTIVE_TIME_READER;
}
- public static KernelCpuProcStringReader getClusterTimeReaderInstance() {
+ static KernelCpuProcStringReader getClusterTimeReaderInstance() {
return CLUSTER_TIME_READER;
}
+ static KernelCpuProcStringReader getUserSysTimeReaderInstance() {
+ return USER_SYS_TIME_READER;
+ }
+
private int mErrors = 0;
private final Path mFile;
private char[] mBuf;
@@ -164,12 +172,12 @@
// ReentrantReadWriteLock allows lock downgrading.
mReadLock.lock();
return new ProcFileIterator(total);
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | NoSuchFileException e) {
mErrors++;
Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile);
} catch (IOException e) {
mErrors++;
- Slog.e(TAG, "Error reading: " + mFile, e);
+ Slog.e(TAG, "Error reading " + mFile, e);
} finally {
StrictMode.setThreadPolicyMask(oldMask);
mWriteLock.unlock();
@@ -193,6 +201,11 @@
mSize = size;
}
+ /** @return Whether there are more lines in the iterator. */
+ public boolean hasNextLine() {
+ return mPos < mSize;
+ }
+
/**
* Fetches the next line. Note that all subsequent return values share the same char[]
* under the hood.
@@ -214,44 +227,6 @@
return CharBuffer.wrap(mBuf, start, i - start);
}
- /**
- * Fetches the next line, converts all numbers into long, and puts into the given long[].
- * To avoid GC, caller should try to use the same array for all calls. All non-numeric
- * chars are treated as delimiters. All numbers are non-negative.
- *
- * @param array An array to store the parsed numbers.
- * @return The number of elements written to the given array. -1 if there is no more line.
- */
- public int nextLineAsArray(long[] array) {
- CharBuffer buf = nextLine();
- if (buf == null) {
- return -1;
- }
- int count = 0;
- long num = -1;
- char c;
-
- while (buf.remaining() > 0 && count < array.length) {
- c = buf.get();
- if (num < 0) {
- if (isNumber(c)) {
- num = c - '0';
- }
- } else {
- if (isNumber(c)) {
- num = num * 10 + c - '0';
- } else {
- array[count++] = num;
- num = -1;
- }
- }
- }
- if (num >= 0) {
- array[count++] = num;
- }
- return count;
- }
-
/** Total size of the proc file in chars. */
public int size() {
return mSize;
@@ -262,8 +237,63 @@
mReadLock.unlock();
}
- private boolean isNumber(char c) {
- return c >= '0' && c <= '9';
+
+ }
+
+ /**
+ * Converts all numbers in the CharBuffer into longs, and puts into the given long[].
+ *
+ * Space and colon are treated as delimiters. All other chars are not allowed. All numbers
+ * are non-negative. To avoid GC, caller should try to use the same array for all calls.
+ *
+ * This method also resets the given buffer to the original position before return so that
+ * it can be read again.
+ *
+ * @param buf The char buffer to be converted.
+ * @param array An array to store the parsed numbers.
+ * @return The number of elements written to the given array. -1 if buf is null, -2 if buf
+ * contains invalid char, -3 if any number overflows.
+ */
+ public static int asLongs(CharBuffer buf, long[] array) {
+ if (buf == null) {
+ return -1;
}
+ final int initialPos = buf.position();
+ int count = 0;
+ long num = -1;
+ char c;
+
+ while (buf.remaining() > 0 && count < array.length) {
+ c = buf.get();
+ if (!(isNumber(c) || c == ' ' || c == ':')) {
+ buf.position(initialPos);
+ return -2;
+ }
+ if (num < 0) {
+ if (isNumber(c)) {
+ num = c - '0';
+ }
+ } else {
+ if (isNumber(c)) {
+ num = num * 10 + c - '0';
+ if (num < 0) {
+ buf.position(initialPos);
+ return -3;
+ }
+ } else {
+ array[count++] = num;
+ num = -1;
+ }
+ }
+ }
+ if (num >= 0) {
+ array[count++] = num;
+ }
+ buf.position(initialPos);
+ return count;
+ }
+
+ private static boolean isNumber(char c) {
+ return c >= '0' && c <= '9';
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
new file mode 100644
index 0000000..7021b57
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Reads per-UID CPU time proc files. Concrete implementations are all nested inside.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} or {@link #readAbsolute} call
+ * within {@link #mMinTimeBetweenRead}. The throttler can be enable / disabled via a param in
+ * the constructor.
+ *
+ * This class and its subclasses are NOT thread-safe and NOT designed to be accessed by more than
+ * one caller since each caller has its own view of delta.
+ *
+ * @param <T> The type of CPU time for the callback.
+ */
+public abstract class KernelCpuUidTimeReader<T> {
+ protected static final boolean DEBUG = false;
+ private static final long DEFAULT_MIN_TIME_BETWEEN_READ = 1000L; // In milliseconds
+
+ final String mTag = this.getClass().getSimpleName();
+ final SparseArray<T> mLastTimes = new SparseArray<>();
+ final KernelCpuProcStringReader mReader;
+ final boolean mThrottle;
+ private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
+ private long mLastReadTimeMs = 0;
+
+ /**
+ * Callback interface for processing each line of the proc file.
+ *
+ * @param <T> The type of CPU time for the callback function.
+ */
+ public interface Callback<T> {
+ /**
+ * @param uid UID of the app
+ * @param time Time spent. The exact data structure depends on subclass implementation.
+ */
+ void onUidCpuTime(int uid, T time);
+ }
+
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ mReader = reader;
+ mThrottle = throttle;
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with a delta of time for each UID.
+ *
+ * @param cb The callback to invoke for each line of the proc file. If null,the data is
+ * consumed and subsequent calls to readDelta will provide a fresh delta.
+ */
+ public void readDelta(@Nullable Callback<T> cb) {
+ if (!mThrottle) {
+ readDeltaImpl(cb);
+ return;
+ }
+ final long currTimeMs = SystemClock.elapsedRealtime();
+ if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (DEBUG) {
+ Slog.d(mTag, "Throttle readDelta");
+ }
+ return;
+ }
+ readDeltaImpl(cb);
+ mLastReadTimeMs = currTimeMs;
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with cumulative time for each UID.
+ *
+ * @param cb The callback to invoke for each line of the proc file. It cannot be null.
+ */
+ public void readAbsolute(Callback<T> cb) {
+ if (!mThrottle) {
+ readAbsoluteImpl(cb);
+ return;
+ }
+ final long currTimeMs = SystemClock.elapsedRealtime();
+ if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (DEBUG) {
+ Slog.d(mTag, "Throttle readAbsolute");
+ }
+ return;
+ }
+ readAbsoluteImpl(cb);
+ mLastReadTimeMs = currTimeMs;
+ }
+
+ abstract void readDeltaImpl(@Nullable Callback<T> cb);
+
+ abstract void readAbsoluteImpl(Callback<T> callback);
+
+ /**
+ * Removes the UID from internal accounting data. This method, overridden in
+ * {@link KernelCpuUidUserSysTimeReader}, also removes the UID from the kernel module.
+ *
+ * @param uid The UID to remove.
+ * @see KernelCpuUidUserSysTimeReader#removeUid(int)
+ */
+ public void removeUid(int uid) {
+ mLastTimes.delete(uid);
+ }
+
+ /**
+ * Removes UIDs in a given range from internal accounting data. This method, overridden in
+ * {@link KernelCpuUidUserSysTimeReader}, also removes the UIDs from the kernel module.
+ *
+ * @param startUid the first uid to remove.
+ * @param endUid the last uid to remove.
+ * @see KernelCpuUidUserSysTimeReader#removeUidsInRange(int, int)
+ */
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (endUid < startUid) {
+ Slog.e(mTag, "start UID " + startUid + " > end UID " + endUid);
+ return;
+ }
+ mLastTimes.put(startUid, null);
+ mLastTimes.put(endUid, null);
+ final int firstIndex = mLastTimes.indexOfKey(startUid);
+ final int lastIndex = mLastTimes.indexOfKey(endUid);
+ mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ }
+
+ /**
+ * Set the minimum time in milliseconds between reads. If throttle is not enabled, this method
+ * has no effect.
+ *
+ * @param minTimeBetweenRead The minimum time in milliseconds.
+ */
+ public void setThrottle(long minTimeBetweenRead) {
+ if (mThrottle && minTimeBetweenRead >= 0) {
+ mMinTimeBetweenRead = minTimeBetweenRead;
+ }
+ }
+
+ /**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+ public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
+
+ // [uid, user_time, system_time, (maybe) power_in_milli-amp-micro_seconds]
+ private final long[] mBuffer = new long[4];
+ // A reusable array to hold [user_time, system_time] for the callback.
+ private final long[] mUsrSysTime = new long[2];
+
+ public KernelCpuUidUserSysTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (iter == null) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) < 3) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[2];
+ mLastTimes.put(uid, lastTimes);
+ }
+ final long currUsrTimeUs = mBuffer[1];
+ final long currSysTimeUs = mBuffer[2];
+ mUsrSysTime[0] = currUsrTimeUs - lastTimes[0];
+ mUsrSysTime[1] = currSysTimeUs - lastTimes[1];
+
+ if (mUsrSysTime[0] < 0 || mUsrSysTime[1] < 0) {
+ Slog.e(mTag, "Negative user/sys time delta for UID=" + uid
+ + "\nPrev times: u=" + lastTimes[0] + " s=" + lastTimes[1]
+ + " Curr times: u=" + currUsrTimeUs + " s=" + currSysTimeUs);
+ } else if (mUsrSysTime[0] > 0 || mUsrSysTime[1] > 0) {
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mUsrSysTime);
+ }
+ }
+ lastTimes[0] = currUsrTimeUs;
+ lastTimes[1] = currSysTimeUs;
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (iter == null) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) < 3) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ mUsrSysTime[0] = mBuffer[1]; // User time in microseconds
+ mUsrSysTime[1] = mBuffer[2]; // System time in microseconds
+ cb.onUidCpuTime((int) mBuffer[0], mUsrSysTime);
+ }
+ }
+ }
+
+ @Override
+ public void removeUid(int uid) {
+ super.removeUid(uid);
+ removeUidsFromKernelModule(uid, uid);
+ }
+
+ @Override
+ public void removeUidsInRange(int startUid, int endUid) {
+ super.removeUidsInRange(startUid, endUid);
+ removeUidsFromKernelModule(startUid, endUid);
+ }
+
+ /**
+ * Removes UIDs in a given range from the kernel module and internal accounting data. Only
+ * {@link BatteryStatsImpl} and its child processes should call this, as the change on
+ * Kernel is
+ * visible system wide.
+ *
+ * @param startUid the first uid to remove
+ * @param endUid the last uid to remove
+ */
+ private void removeUidsFromKernelModule(int startUid, int endUid) {
+ Slog.d(mTag, "Removing uids " + startUid + "-" + endUid);
+ final int oldMask = StrictMode.allowThreadDiskWritesMask();
+ try (FileWriter writer = new FileWriter(REMOVE_UID_PROC_FILE)) {
+ writer.write(startUid + "-" + endUid);
+ writer.flush();
+ } catch (IOException e) {
+ Slog.e(mTag, "failed to remove uids " + startUid + " - " + endUid
+ + " from uid_cputime module", e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+ }
+
+ /**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+ public static class KernelCpuUidFreqTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+ // We check the existence of proc file a few times (just in case it is not ready yet when we
+ // start reading) and if it is not available, we simply ignore further read requests.
+ private static final int MAX_ERROR_COUNT = 5;
+
+ private final Path mProcFilePath;
+ private long[] mBuffer;
+ private long[] mCurTimes;
+ private long[] mDeltaTimes;
+ private long[] mCpuFreqs;
+
+ private int mFreqCount = 0;
+ private int mErrors = 0;
+ private boolean mPerClusterTimesAvailable;
+ private boolean mAllUidTimesAvailable = true;
+
+ public KernelCpuUidFreqTimeReader(boolean throttle) {
+ this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
+ throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
+ boolean throttle) {
+ super(reader, throttle);
+ mProcFilePath = Paths.get(procFile);
+ }
+
+ /**
+ * @return Whether per-cluster times are available.
+ */
+ public boolean perClusterTimesAvailable() {
+ return mPerClusterTimesAvailable;
+ }
+
+ /**
+ * @return Whether all-UID times are available.
+ */
+ public boolean allUidTimesAvailable() {
+ return mAllUidTimesAvailable;
+ }
+
+ /**
+ * @return A map of all UIDs to their CPU time-in-state array in milliseconds.
+ */
+ public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
+ return mLastTimes;
+ }
+
+ /**
+ * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
+ * to determine if per-cluster times are available.
+ *
+ * @param powerProfile The PowerProfile to compare against.
+ * @return A long[] of CPU frequencies in Hz.
+ */
+ public long[] readFreqs(@NonNull PowerProfile powerProfile) {
+ checkNotNull(powerProfile);
+ if (mCpuFreqs != null) {
+ // No need to read cpu freqs more than once.
+ return mCpuFreqs;
+ }
+ if (!mAllUidTimesAvailable) {
+ return null;
+ }
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+ if (readFreqs(reader.readLine()) == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ if (++mErrors >= MAX_ERROR_COUNT) {
+ mAllUidTimesAvailable = false;
+ }
+ Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ // Check if the freqs in the proc file correspond to per-cluster freqs.
+ final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
+ final int numClusters = powerProfile.getNumCpuClusters();
+ if (numClusterFreqs.size() == numClusters) {
+ mPerClusterTimesAvailable = true;
+ for (int i = 0; i < numClusters; ++i) {
+ if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
+ mPerClusterTimesAvailable = false;
+ break;
+ }
+ }
+ } else {
+ mPerClusterTimesAvailable = false;
+ }
+ Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
+ return mCpuFreqs;
+ }
+
+ private long[] readFreqs(String line) {
+ if (line == null) {
+ return null;
+ }
+ final String[] lineArray = line.split(" ");
+ if (lineArray.length <= 1) {
+ Slog.wtf(mTag, "Malformed freq line: " + line);
+ return null;
+ }
+ mFreqCount = lineArray.length - 1;
+ mCpuFreqs = new long[mFreqCount];
+ mCurTimes = new long[mFreqCount];
+ mDeltaTimes = new long[mFreqCount];
+ mBuffer = new long[mFreqCount + 1];
+ for (int i = 0; i < mFreqCount; ++i) {
+ mCpuFreqs[i] = Long.parseLong(lineArray[i + 1], 10);
+ }
+ return mCpuFreqs;
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mFreqCount];
+ mLastTimes.put(uid, lastTimes);
+ }
+ copyToCurTimes();
+ boolean notify = false;
+ boolean valid = true;
+ for (int i = 0; i < mFreqCount; i++) {
+ // Unit is 10ms.
+ mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+ if (mDeltaTimes[i] < 0) {
+ Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ valid = false;
+ }
+ notify |= mDeltaTimes[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTimes);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ copyToCurTimes();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+ }
+ }
+ }
+
+ private void copyToCurTimes() {
+ for (int i = 0; i < mFreqCount; i++) {
+ mCurTimes[i] = mBuffer[i + 1] * 10;
+ }
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mCpuFreqs != null) {
+ return true;
+ }
+ return readFreqs(line.toString()) != null;
+ }
+
+ /**
+ * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
+ * read from the proc file.
+ *
+ * We need to assume that freqs in each cluster are strictly increasing.
+ * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
+ * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
+ *
+ * @return an IntArray filled with no. of freqs in each cluster.
+ */
+ private IntArray extractClusterInfoFromProcFileFreqs() {
+ final IntArray numClusterFreqs = new IntArray();
+ int freqsFound = 0;
+ for (int i = 0; i < mFreqCount; ++i) {
+ freqsFound++;
+ if (i + 1 == mFreqCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
+ numClusterFreqs.add(freqsFound);
+ freqsFound = 0;
+ }
+ }
+ return numClusterFreqs;
+ }
+ }
+
+ /**
+ * Reads /proc/uid_concurrent_active_time and reports CPU active time to BatteryStats to
+ * compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+ *
+ * /proc/uid_concurrent_active_time has the following format:
+ * cpus: n
+ * uid0: time0a, time0b, ..., time0n,
+ * uid1: time1a, time1b, ..., time1n,
+ * uid2: time2a, time2b, ..., time2n,
+ * ...
+ * where n is the total number of cpus (num_possible_cpus)
+ * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
+ *
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+ public static class KernelCpuUidActiveTimeReader extends KernelCpuUidTimeReader<Long> {
+ private int mCores = 0;
+ private long[] mBuffer;
+
+ public KernelCpuUidActiveTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<Long> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ int uid = (int) mBuffer[0];
+ long cpuActiveTime = sumActiveTime(mBuffer);
+ if (cpuActiveTime > 0) {
+ long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+ if (delta > 0) {
+ mLastTimes.put(uid, cpuActiveTime);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, delta);
+ }
+ } else if (delta < 0) {
+ Slog.e(mTag, "Negative delta from active time proc: " + delta);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<Long> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ long cpuActiveTime = sumActiveTime(mBuffer);
+ if (cpuActiveTime > 0) {
+ cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+ }
+ }
+ }
+ }
+
+ private static long sumActiveTime(long[] times) {
+ // UID is stored at times[0].
+ double sum = 0;
+ for (int i = 1; i < times.length; i++) {
+ sum += (double) times[i] * 10 / i; // Unit is 10ms.
+ }
+ return (long) sum;
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mCores > 0) {
+ return true;
+ }
+
+ String str = line.toString();
+ if (!str.startsWith("cpus:")) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+ return false;
+ }
+ int cores = Integer.parseInt(str.substring(5).trim(), 10);
+ if (cores <= 0) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+ return false;
+ }
+ mCores = cores;
+ mBuffer = new long[mCores + 1]; // UID is stored at mBuffer[0].
+ return true;
+ }
+ }
+
+
+ /**
+ * Reads /proc/uid_concurrent_policy_time and reports CPU cluster times to BatteryStats to
+ * compute cluster power. See {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+ *
+ * /proc/uid_concurrent_policy_time has the following format:
+ * policyX: x policyY: y policyZ: z...
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n,
+ * ...
+ * The first line lists all policies (i.e. clusters) followed by # cores in each policy.
+ * Each uid is followed by x time entries corresponding to the time it spent on clusterX
+ * running concurrently with 0, 1, 2, ..., x - 1 other processes, then followed by y, z, ...
+ * time entries.
+ *
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+ public static class KernelCpuUidClusterTimeReader extends KernelCpuUidTimeReader<long[]> {
+ private int mNumClusters;
+ private int mNumCores;
+ private int[] mCoresOnClusters; // # cores on each cluster.
+ private long[] mBuffer; // To store data returned from ProcFileIterator.
+ private long[] mCurTime;
+ private long[] mDeltaTime;
+
+ public KernelCpuUidClusterTimeReader(boolean throttle) {
+ super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+ }
+
+ @VisibleForTesting
+ public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ super(reader, throttle);
+ }
+
+ @Override
+ void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mNumClusters];
+ mLastTimes.put(uid, lastTimes);
+ }
+ sumClusterTime();
+ boolean valid = true;
+ boolean notify = false;
+ for (int i = 0; i < mNumClusters; i++) {
+ mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+ if (mDeltaTime[i] < 0) {
+ Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ valid = false;
+ }
+ notify |= mDeltaTime[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTime);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ void readAbsoluteImpl(Callback<long[]> cb) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ if (!checkPrecondition(iter)) {
+ return;
+ }
+ CharBuffer buf;
+ while ((buf = iter.nextLine()) != null) {
+ if (asLongs(buf, mBuffer) != mBuffer.length) {
+ Slog.wtf(mTag, "Invalid line: " + buf.toString());
+ continue;
+ }
+ sumClusterTime();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+ }
+ }
+ }
+
+ private void sumClusterTime() {
+ // UID is stored at mBuffer[0].
+ int core = 1;
+ for (int i = 0; i < mNumClusters; i++) {
+ double sum = 0;
+ for (int j = 1; j <= mCoresOnClusters[i]; j++) {
+ sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+ }
+ mCurTime[i] = (long) sum;
+ }
+ }
+
+ private boolean checkPrecondition(ProcFileIterator iter) {
+ if (iter == null || !iter.hasNextLine()) {
+ // Error logged in KernelCpuProcStringReader.
+ return false;
+ }
+ CharBuffer line = iter.nextLine();
+ if (mNumClusters > 0) {
+ return true;
+ }
+ // Parse # cores in clusters.
+ String[] lineArray = line.toString().split(" ");
+ if (lineArray.length % 2 != 0) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+ return false;
+ }
+ int[] clusters = new int[lineArray.length / 2];
+ int cores = 0;
+ for (int i = 0; i < clusters.length; i++) {
+ if (!lineArray[i * 2].startsWith("policy")) {
+ Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+ return false;
+ }
+ clusters[i] = Integer.parseInt(lineArray[i * 2 + 1], 10);
+ cores += clusters[i];
+ }
+ mNumClusters = clusters.length;
+ mNumCores = cores;
+ mCoresOnClusters = clusters;
+ mBuffer = new long[cores + 1];
+ mCurTime = new long[mNumClusters];
+ mDeltaTime = new long[mNumClusters];
+ return true;
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9a7094a..69ba070 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -66,7 +66,7 @@
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
void onNotificationDirectReplied(String key);
void onNotificationSmartRepliesAdded(in String key, in int replyCount);
- void onNotificationSmartReplySent(in String key, in int replyIndex);
+ void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int vis, int mask, String cause);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 15745e9..8495850 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.Environment;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.text.TextUtils;
@@ -930,6 +931,16 @@
XmlUtils.skipCurrentTag(parser);
}
}
+ // If the storage model feature flag is disabled, we need to fiddle
+ // around with permission definitions to return us to pre-Q behavior.
+ // STOPSHIP(b/112545973): remove once feature enabled by default
+ if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
+ newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
+ newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
+ return;
+ }
+ }
if (!newPermissions.isEmpty()) {
mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e064423..8c5b6f4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -794,7 +794,8 @@
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
- android:protectionLevel="normal" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -814,7 +815,8 @@
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
- android:protectionLevel="normal" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Runtime permission controlling access to the user's shared aural media
collection. -->
@@ -831,12 +833,6 @@
android:description="@string/permdesc_audioRead"
android:protectionLevel="dangerous" />
- <!-- Allows an application to modify the user's shared audio collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_AUDIO"
- android:label="@string/permlab_audioWrite"
- android:description="@string/permdesc_audioWrite"
- android:protectionLevel="dangerous" />
-
<!-- Runtime permission controlling access to the user's shared visual media
collection, including images and videos. -->
<permission-group android:name="android.permission-group.MEDIA_VISUAL"
@@ -852,24 +848,12 @@
android:description="@string/permdesc_imagesRead"
android:protectionLevel="dangerous" />
- <!-- Allows an application to modify the user's shared images collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_IMAGES"
- android:label="@string/permlab_imagesWrite"
- android:description="@string/permdesc_imagesWrite"
- android:protectionLevel="dangerous" />
-
<!-- Allows an application to read the user's shared video collection. -->
<permission android:name="android.permission.READ_MEDIA_VIDEO"
android:label="@string/permlab_videoRead"
android:description="@string/permdesc_videoRead"
android:protectionLevel="dangerous" />
- <!-- Allows an application to modify the user's shared video collection. -->
- <permission android:name="android.permission.WRITE_MEDIA_VIDEO"
- android:label="@string/permlab_videoWrite"
- android:description="@string/permdesc_videoWrite"
- android:protectionLevel="dangerous" />
-
<!-- Allows an application to access any geographic locations persisted in the
user's shared collection. -->
<permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
@@ -3024,6 +3008,13 @@
<permission android:name="android.permission.BIND_TV_INPUT"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi
Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
to ensure that only the system can bind to it.
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index 3c9f6ee..c024dbe 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -16,16 +16,14 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ style="@android:style/NotificationAction"
android:id="@+id/action0"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center"
android:gravity="start|center_vertical"
android:layout_marginStart="4dp"
- android:textColor="@color/notification_default_color"
android:singleLine="true"
android:textAlignment="viewStart"
android:ellipsize="end"
- android:background="@drawable/notification_material_action_background"
/>
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 07559f4..4258019 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -18,6 +18,7 @@
android:id="@+id/actions_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_action_list_margin_top"
android:layout_gravity="bottom">
<com.android.internal.widget.NotificationActionListLayout
android:id="@+id/actions"
@@ -27,6 +28,7 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:visibility="gone"
+ android:background="@color/notification_action_list_background_color"
>
<!-- actions will be added here -->
</com.android.internal.widget.NotificationActionListLayout>
diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml
index 9fa7c6a..f165724 100644
--- a/core/res/res/layout/notification_material_action_tombstone.xml
+++ b/core/res/res/layout/notification_material_action_tombstone.xml
@@ -16,7 +16,7 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ style="@android:style/NotificationTombstoneAction"
android:id="@+id/action0"
android:layout_width="wrap_content"
android:layout_height="48dp"
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 9de8842..a2ad3b9 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
<!-- Color palette -->
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/white</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 605662a..6c4861b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7941,6 +7941,11 @@
<!-- Uri that specifies a settings Slice for this wallpaper. -->
<attr name="settingsSliceUri" />
+ <!-- Indicates that this wallpaper service can support multiple engines to render on each
+ surface independently. An example use case is a multi-display set-up where the
+ wallpaper service can render surfaces to each of the connected displays. -->
+ <attr name="supportsMultipleDisplays" format="boolean" />
+
</declare-styleable>
<!-- Use <code>dream</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4122cf0..16c0744 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -146,10 +146,14 @@
<color name="notification_default_color">#757575</color> <!-- Gray 600 -->
+ <color name="notification_action_button_text_color">@color/notification_default_color</color>
+
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
<color name="notification_action_list">#ffeeeeee</color>
+ <color name="notification_action_list_background_color">@null</color>
+
<!-- Keyguard colors -->
<color name="keyguard_avatar_frame_color">#ffffffff</color>
<color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e902989..f7b9961 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -200,6 +200,9 @@
<!-- The height of the notification action list -->
<dimen name="notification_action_list_height">60dp</dimen>
+ <!-- The margin of the notification action list at the top -->
+ <dimen name="notification_action_list_margin_top">0dp</dimen>
+
<!-- The height of the notification action list -->
<dimen name="notification_action_emphasized_height">48dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73dae08..3373d14 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2920,6 +2920,7 @@
<public name="shell" />
<public name="interactiveUiTimeout" />
<public name="importantForContentCapture" />
+ <public name="supportsMultipleDisplays" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 200c35d..bd6d976 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1586,22 +1586,14 @@
<string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permlab_sdcardRead" product="nosdcard">read the contents of your USB storage</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardRead" product="default">read the contents of your SD card</string>
+ <string name="permlab_sdcardRead">read the contents of your shared storage</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to read the contents of your USB storage.</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardRead" product="default">Allows the app to read the contents of your SD card.</string>
+ <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permlab_sdcardWrite" product="nosdcard">modify or delete the contents of your USB storage</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_sdcardWrite" product="default">modify or delete the contents of your SD card</string>
+ <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permdesc_sdcardWrite" product="nosdcard">Allows the app to write to the USB storage.</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
+ <string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_use_sip">make/receive SIP calls</string>
@@ -2989,6 +2981,18 @@
<!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
<string name="view_flight_desc">Track selected flight</string>
+ <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
+ <string name="translate">Translate</string>
+
+ <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
+ <string name="translate_desc">Translate selected text</string>
+
+ <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
+ <string name="define">Define</string>
+
+ <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
+ <string name="define_desc">Define selected text</string>
+
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
@@ -3867,10 +3871,8 @@
<string name="action_mode_done">Done</string>
<!-- Strings for MasterClearReceiver. -->
- <!-- Text for progress dialog while erasing USB storage volume [CHAR LIMIT=NONE] -->
- <string name="progress_erasing" product="nosdcard">Erasing USB storage\u2026</string>
- <!-- Text for progress dialog while erasing SD card [CHAR LIMIT=NONE] -->
- <string name="progress_erasing" product="default">Erasing SD card\u2026</string>
+ <!-- Text for progress dialog while erasing the shared storage volume [CHAR LIMIT=NONE] -->
+ <string name="progress_erasing">Erasing shared storage\u2026</string>
<!-- Text for WebView's text selection Action Mode -->
<!-- ActionBar action to share the current selection [CHAR LIMIT=10] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bd53936..18f7e48 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1526,10 +1526,22 @@
<item name="gravity">top</item>
</style>
- <!-- Colored bordered ink button -->
+ <!-- The style for normal action button on notification -->
+ <style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
+ <item name="textColor">@color/notification_action_button_text_color</item>
+ <item name="background">@drawable/notification_material_action_background</item>
+ </style>
+
+ <!-- The style for emphasized action button on notification: Colored bordered ink button -->
<style name="NotificationEmphasizedAction" parent="Widget.Material.Button">
<item name="background">@drawable/btn_notification_emphasized</item>
<item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item>
</style>
+ <!-- The style for disabled action button on notification -->
+ <style name="NotificationTombstoneAction" parent="NotificationAction">
+ <item name="textColor">#555555</item>
+ </style>
+
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 82c9ff3..9264f90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,10 @@
<java-symbol type="string" name="add_calendar_event_desc" />
<java-symbol type="string" name="view_flight" />
<java-symbol type="string" name="view_flight_desc" />
+ <java-symbol type="string" name="translate" />
+ <java-symbol type="string" name="translate_desc" />
+ <java-symbol type="string" name="define" />
+ <java-symbol type="string" name="define_desc" />
<java-symbol type="string" name="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a317c99..8ac9451d 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -364,9 +364,7 @@
private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
// Flags explicitly over ride everything else.
- if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- return INSTALL_LOC_SD;
- } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+ if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
return INSTALL_LOC_INT;
}
// Manifest option takes precedence next
@@ -437,8 +435,6 @@
int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
if (rLoc == INSTALL_LOC_INT) {
- assertFalse(
- (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
assertEquals(appInstallPath, srcPath);
assertEquals(appInstallPath, publicSrcPath);
assertStartsWith("Native library should point to shared lib directory",
@@ -461,8 +457,6 @@
}
}
} else if (rLoc == INSTALL_LOC_SD) {
- assertFalse("The application should not be installed forward locked",
- (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
assertTrue("Application flags (" + info.flags
+ ") should contain FLAG_EXTERNAL_STORAGE",
(info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -845,31 +839,10 @@
}
@LargeTest
- public void testReplaceFailSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
- }
-
- @LargeTest
public void testReplaceNormalInternal() throws Exception {
sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
}
- @LargeTest
- public void testReplaceSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING
- | PackageManager.INSTALL_EXTERNAL);
- }
-
/* -------------- Delete tests --- */
private static class DeleteObserver extends IPackageDeleteObserver.Stub {
private CountDownLatch mLatch = new CountDownLatch(1);
@@ -1015,31 +988,12 @@
deleteFromRawResource(0, 0);
}
- @LargeTest
- public void testDeleteSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
- }
@LargeTest
public void testDeleteNormalInternalRetainData() throws Exception {
deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
}
- @LargeTest
- public void testDeleteSdcardRetainData() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
- }
-
void cleanUpInstall(InstallParams ip) throws Exception {
if (ip == null) {
return;
@@ -1104,60 +1058,6 @@
0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
}
- /*
- * Install a package on internal flash via PackageManager install flag. Replace
- * the package via flag to install on sdcard. Make sure the new flag overrides
- * the old install location.
- */
- @LargeTest
- public void testReplaceFlagInternalSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = 0;
- int rFlags = PackageManager.INSTALL_EXTERNAL;
- InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
- int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
- try {
- invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
- assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
- } finally {
- cleanUpInstall(ip);
- }
- }
-
- /*
- * Install a package on sdcard via PackageManager install flag. Replace
- * the package with no flags or manifest option and make sure the old
- * install location is retained.
- */
- @LargeTest
- public void testReplaceFlagSdcardInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = 0;
- InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
- int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
- try {
- invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
- } catch (Exception e) {
- failStr("Failed with exception : " + e);
- } finally {
- cleanUpInstall(ip);
- }
- }
-
@LargeTest
public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
// Do not run on devices with emulated external storage.
@@ -1375,34 +1275,6 @@
}
@LargeTest
- public void testMoveAppExternalToExternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int installFlags = PackageManager.INSTALL_EXTERNAL;
- int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
- boolean fail = true;
- int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
- }
-
- @LargeTest
- public void testMoveAppExternalToInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int installFlags = PackageManager.INSTALL_EXTERNAL;
- int moveFlags = PackageManager.MOVE_INTERNAL;
- boolean fail = false;
- int result = PackageManager.MOVE_SUCCEEDED;
- sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
- }
-
- @LargeTest
public void testMoveAppFailInternalToExternalDelete() throws Exception {
// Do not run on devices with emulated external storage.
if (Environment.isExternalStorageEmulated()) {
@@ -1458,19 +1330,6 @@
}
/*
- * Install an app on sdcard.
- */
- @LargeTest
- public void testFlagE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
- }
-
- /*
* Install an app with both internal and manifest option set.
* should install on internal.
*/
@@ -1506,59 +1365,6 @@
false, -1,
PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
- /*
- * Install an app with both external and manifest option set.
- * should install externally.
- */
- @LargeTest
- public void testFlagEManifestI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_internal,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- /*
- * Install an app with both external and manifest preference for
- * preferExternal. Should install externally.
- */
- @LargeTest
- public void testFlagEManifestE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- /*
- * Install an app with both external and manifest preference for
- * auto. should install on external media.
- */
- @LargeTest
- public void testFlagEManifestA() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- installFromRawResource("install.apk", R.raw.install_loc_auto,
- PackageManager.INSTALL_EXTERNAL,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
/*
* The following test functions verify install location for existing apps.
@@ -1586,75 +1392,6 @@
-1);
}
- @LargeTest
- public void testFlagIExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
- @LargeTest
- public void testFlagEExistingI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_INTERNAL;
- int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
- @LargeTest
- public void testFlagEExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install,
- rFlags,
- true,
- false, -1,
- -1);
- }
-
/*
* The following set of tests verify the installation of apps with
* install location attribute set to internalOnly, preferExternal and auto.
@@ -1720,29 +1457,6 @@
}
@LargeTest
- public void testManifestIExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_internal,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- }
-
- @LargeTest
public void testManifestEExistingI() throws Exception {
// Do not run on devices with emulated external storage.
if (Environment.isExternalStorageEmulated()) {
@@ -1766,29 +1480,6 @@
}
@LargeTest
- public void testManifestEExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
public void testManifestAExistingI() throws Exception {
int iFlags = PackageManager.INSTALL_INTERNAL;
int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
@@ -1806,29 +1497,6 @@
PackageInfo.INSTALL_LOCATION_AUTO);
}
- @LargeTest
- public void testManifestAExistingE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
- // First install.
- installFromRawResource("install.apk", R.raw.install,
- iFlags,
- false,
- false, -1,
- -1);
- // Replace now
- installFromRawResource("install.apk", R.raw.install_loc_auto,
- rFlags,
- true,
- false, -1,
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
/*
* The following set of tests check install location for existing
* application based on user setting.
@@ -1896,42 +1564,6 @@
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
- @LargeTest
- public void testExistingEUserI() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
- public void testExistingEUserE() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
- @LargeTest
- public void testExistingEUserA() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
- int iFlags = PackageManager.INSTALL_EXTERNAL;
- setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- }
-
/*
* The following set of tests verify that the user setting defines
* the install location.
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4802ebe..2c001c9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -124,6 +124,7 @@
Settings.Global.AUTOFILL_LOGGING_LEVEL,
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,
Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
@@ -283,6 +284,7 @@
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
new file mode 100644
index 0000000..f0faaf6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActionsSuggestionsHelperTest {
+ @Test
+ public void testToNativeMessages_emptyInput() {
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+
+ assertThat(conversationMessages).isEmpty();
+ }
+
+ @Test
+ public void testToNativeMessages_noTextMessages() {
+ ConversationActions.Message messageWithoutText =
+ new ConversationActions.Message.Builder().build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Collections.singletonList(messageWithoutText));
+
+ assertThat(conversationMessages).isEmpty();
+ }
+
+ @Test
+ public void testToNativeMessages_missingPersonInFirstMessage() {
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder()
+ .setText("first")
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .setAuthor(new Person.Builder().build())
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage));
+
+ assertThat(conversationMessages).hasLength(2);
+ assertNativeMessage(conversationMessages[0], secondMessage.getText(), 1);
+ assertNativeMessage(conversationMessages[1], thirdMessage.getText(), 0);
+ }
+
+ @Test
+ public void testToNativeMessages_missingPersonInMiddleOfConversation() {
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder()
+ .setText("first")
+ .setAuthor(new Person.Builder().setName("first").build())
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(new Person.Builder().setName("third").build())
+ .build();
+ ConversationActions.Message fourthMessage =
+ new ConversationActions.Message.Builder()
+ .setText("fourth")
+ .setAuthor(new Person.Builder().setName("fourth").build())
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+ assertThat(conversationMessages).hasLength(2);
+ assertNativeMessage(conversationMessages[0], thirdMessage.getText(), 2);
+ assertNativeMessage(conversationMessages[1], fourthMessage.getText(), 1);
+ }
+
+ @Test
+ public void testToNativeMessages_userIdEncoding() {
+ Person userA = new Person.Builder().setName("userA").build();
+ Person userB = new Person.Builder().setName("userB").build();
+
+ ConversationActions.Message firstMessage =
+ new ConversationActions.Message.Builder()
+ .setText("first")
+ .setAuthor(userB)
+ .build();
+ ConversationActions.Message secondMessage =
+ new ConversationActions.Message.Builder()
+ .setText("second")
+ .setAuthor(userA)
+ .build();
+ ConversationActions.Message thirdMessage =
+ new ConversationActions.Message.Builder()
+ .setText("third")
+ .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+ .build();
+ ConversationActions.Message fourthMessage =
+ new ConversationActions.Message.Builder()
+ .setText("fourth")
+ .setAuthor(userA)
+ .build();
+
+ ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+ ActionsSuggestionsHelper.toNativeMessages(
+ Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+ assertThat(conversationMessages).hasLength(4);
+ assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2);
+ assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1);
+ assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0);
+ assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1);
+ }
+
+ private static void assertNativeMessage(
+ ActionsSuggestionsModel.ConversationMessage nativeMessage,
+ CharSequence text,
+ int userId) {
+ assertThat(nativeMessage.getText()).isEqualTo(text.toString());
+ assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
new file mode 100644
index 0000000..0180856
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A builder used to build a fake context for testing.
+ */
+// TODO: Consider making public.
+final class FakeContextBuilder {
+
+ /**
+ * A component name that can be used for tests.
+ */
+ public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls");
+
+ private final PackageManager mPackageManager;
+ private final ContextWrapper mContext;
+ private final Map<String, ComponentName> mComponents = new HashMap<>();
+ private @Nullable ComponentName mAllIntentComponent;
+
+ FakeContextBuilder() {
+ mPackageManager = mock(PackageManager.class);
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
+ mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+ };
+ }
+
+ /**
+ * Sets the component name of an activity to handle the specified intent action.
+ * <p>
+ * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+ */
+ public FakeContextBuilder setIntentComponent(
+ String intentAction, @Nullable ComponentName component) {
+ Preconditions.checkNotNull(intentAction);
+ mComponents.put(intentAction, component);
+ return this;
+ }
+
+
+ /**
+ * Sets the component name of an activity to handle all intents.
+ * <p>
+ * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+ */
+ public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) {
+ mAllIntentComponent = component;
+ return this;
+ }
+
+ /**
+ * Builds and returns a fake context.
+ */
+ public Context build() {
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenAnswer(
+ (Answer<ResolveInfo>) invocation -> {
+ final String action = ((Intent) invocation.getArgument(0)).getAction();
+ final ComponentName component = mComponents.containsKey(action)
+ ? mComponents.get(action)
+ : mAllIntentComponent;
+ return getResolveInfo(component);
+ });
+ return mContext;
+ }
+
+ /**
+ * Returns a component name with random package and class names.
+ */
+ public static ComponentName newComponent() {
+ return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+ }
+
+ private static ResolveInfo getResolveInfo(ComponentName component) {
+ final ResolveInfo info;
+ if (component == null) {
+ info = null;
+ } else {
+ // NOTE: If something breaks in TextClassifier because we expect more fields to be set
+ // in here, just add them.
+ info = new ResolveInfo();
+ info.activityInfo = new ActivityInfo();
+ info.activityInfo.packageName = component.getPackageName();
+ info.activityInfo.name = component.getClassName();
+ info.activityInfo.exported = true;
+ info.activityInfo.applicationInfo = new ApplicationInfo();
+ info.activityInfo.applicationInfo.icon = 0;
+ }
+ return info;
+ }
+
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
new file mode 100644
index 0000000..bae2be3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.AnnotatorModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentFactoryTest {
+
+ private static final String TEXT = "text";
+
+ @Test
+ public void create_typeDictionary() {
+ AnnotatorModel.ClassificationResult classificationResult =
+ new AnnotatorModel.ClassificationResult(
+ TextClassifier.TYPE_DICTIONARY,
+ 1.0f,
+ null,
+ null);
+
+ List<TextClassifierImpl.LabeledIntent> intents = TextClassifierImpl.IntentFactory.create(
+ InstrumentationRegistry.getContext(),
+ TEXT,
+ false,
+ null,
+ classificationResult);
+
+ assertThat(intents).hasSize(1);
+ TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
+ Intent intent = labeledIntent.getIntent();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+ assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 46aa5b4..a3f69d9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -20,18 +20,10 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.LocaleList;
import android.service.textclassifier.TextClassifierService;
import android.support.test.InstrumentationRegistry;
@@ -41,7 +33,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -78,23 +69,10 @@
@Test
public void testCannotResolveIntent() {
- final PackageManager fakePackageMgr = mock(PackageManager.class);
-
- ResolveInfo validInfo = mContext.getPackageManager().resolveActivity(
- new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0);
- // Make packageManager fail when it gets the following intent:
- ArgumentMatcher<Intent> toFailIntent =
- intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT);
-
- when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo);
- when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null);
-
- ContextWrapper fakeContext = new ContextWrapper(mContext) {
- @Override
- public PackageManager getPackageManager() {
- return fakePackageMgr;
- }
- };
+ Context fakeContext = new FakeContextBuilder()
+ .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
+ .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
+ .build();
TextClassifier fallback = TextClassifier.NO_OP;
TextClassifier classifier = new TextClassifierImpl(
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 06ba15e..2ec35e9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -26,6 +26,8 @@
import android.os.LocaleList;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -52,7 +54,11 @@
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object> textClassifierTypes() {
- return Arrays.asList(LOCAL, SYSTEM);
+ return Arrays.asList(LOCAL);
+
+ // TODO: The following will fail on any device that specifies a no-op TextClassifierService.
+ // Enable when we can set a specified TextClassifierService for testing.
+ // return Arrays.asList(LOCAL, SYSTEM);
}
@Parameterized.Parameter
@@ -296,6 +302,17 @@
assertTrue(links.getLinks().isEmpty());
}
+ @Test
+ public void testApplyLinks_unsupportedCharacter() {
+ if (isTextClassifierDisabled()) return;
+ Spannable url = new SpannableString("\u202Emoc.diordna.com");
+ TextLinks.Request request = new TextLinks.Request.Builder(url).build();
+ assertEquals(
+ TextLinks.STATUS_UNSUPPORTED_CHARACTER,
+ mClassifier.generateLinks(request).apply(url, 0, null));
+ }
+
+
@Test(expected = IllegalArgumentException.class)
public void testGenerateLinks_tooLong() {
if (isTextClassifierDisabled()) {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 70dc618..90758ba 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -983,6 +983,19 @@
}
@Test
+ public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
+ useSystemDefaultTextClassifier();
+ final String text = "\u202Emoc.diordna.com";
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ mActivityRule.runOnUiThread(() -> textView.setText(text));
+ mInstrumentation.waitForIdleSync();
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+ }
+
+ @Test
public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable {
final List<SelectionEvent> selectionEvents = new ArrayList<>();
final TextClassifier classifier = new TextClassifier() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 3cfc644..225515e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,6 +39,10 @@
BatteryStatsUserLifecycleTests.class,
KernelCpuProcReaderTest.class,
KernelCpuProcStringReaderTest.class,
+ KernelCpuUidActiveTimeReaderTest.class,
+ KernelCpuUidClusterTimeReaderTest.class,
+ KernelCpuUidFreqTimeReaderTest.class,
+ KernelCpuUidUserSysTimeReaderTest.class,
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
KernelUidCpuFreqTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index dae9eb5..2663f2b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -37,6 +37,7 @@
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.nio.CharBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
@@ -149,7 +150,7 @@
+ "0 0 1 1 1 0 2 0 221",
iter.nextLine().toString());
long[] actual = new long[43];
- iter.nextLineAsArray(actual);
+ KernelCpuProcStringReader.asLongs(iter.nextLine(), actual);
assertArrayEquals(
new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
@@ -183,7 +184,7 @@
}
}
- /** Tests nextLineToArray functionality. */
+ /** Tests reading lines, then converting to long[]. */
@Test
public void testReadLineToArray() throws Exception {
final long[][] data = getTestArray(800, 50);
@@ -193,12 +194,32 @@
long[] actual = new long[50];
try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
for (long[] expected : data) {
- assertEquals(50, iter.nextLineAsArray(actual));
+ CharBuffer cb = iter.nextLine();
+ String before = cb.toString();
+ assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual));
assertArrayEquals(expected, actual);
+ assertEquals("Buffer not reset to the pos before reading", before, cb.toString());
}
}
}
+ /** Tests error handling of converting to long[]. */
+ @Test
+ public void testLineToArrayErrorHandling() {
+ long[] actual = new long[100];
+ String invalidChar = "123: -1234 456";
+ String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123";
+ CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length());
+ assertEquals("Failed to report err for: " + invalidChar, -2,
+ KernelCpuProcStringReader.asLongs(cb, actual));
+ assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString());
+
+ cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length());
+ assertEquals("Failed to report err for: " + overflow, -3,
+ KernelCpuProcStringReader.asLongs(cb, actual));
+ assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString());
+ }
+
/**
* Tests that reading a file over the limit (1MB) will return null.
*/
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
new file mode 100644
index 0000000..adafda0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidActiveTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidActiveTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidActiveTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int mCpus = 4;
+ private final String mHeadline = "cpus: 4\n";
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidActiveTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] newTimes1 = increaseTime(times);
+ writeToFile(mHeadline + uidLines(mUids, newTimes1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] newTimes2 = increaseTime(newTimes1);
+ writeToFile(mHeadline + uidLines(mUids, newTimes2));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] newTimes3 = increaseTime(newTimes2);
+ writeToFile(mHeadline + uidLines(mUids, newTimes3));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaDecreasedTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+ times2[0][0] = 100;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testReadDeltaNegativeTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time is -ve.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] *= -1;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long getActiveTime(long[] times) {
+ return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> {
+ SparseLongArray mData = new SparseLongArray();
+
+ public void verify(int uid, long time) {
+ assertEquals(time, mData.get(uid));
+ mData.delete(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, Long time) {
+ mData.put(uid, time);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
new file mode 100644
index 0000000..ad20d84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidClusterTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidClusterTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidClusterTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int mCpus = 6;
+ private final String mHeadline = "policy0: 4 policy4: 2\n";
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidClusterTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], clusterTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] times4 = increaseTime(times3);
+ writeToFile(mHeadline + uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], clusterTime(times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], clusterTime(times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaDecreasedTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+ times2[0][0] = 100;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testReadDeltaNegativeTime() throws Exception {
+ final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+ writeToFile(mHeadline + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] *= -1;
+ writeToFile(mHeadline + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(mHeadline + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+ }
+ mCallback.verifyNoMoreInteractions();
+ }
+
+ private long[] clusterTime(long[] times) {
+ // Assumes 4 + 2 cores
+ return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
+ times[4] + times[5] / 2};
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
new file mode 100644
index 0000000..1d3a98a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidFreqTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidFreqTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidFreqTimeReader mReader;
+ private VerifiableCallback mCallback;
+ @Mock
+ private PowerProfile mPowerProfile;
+
+ private Random mRand = new Random(12345);
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
+ final long[][] freqs = {
+ {1, 12, 123, 1234},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
+ {1, 12, 123, 23, 2345, 234567}
+ };
+ final int[] numClusters = {2, 2, 3, 1};
+ final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
+ for (int i = 0; i < freqs.length; ++i) {
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+ writeToFile(freqsLine(freqs[i]));
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+ Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+ assertFalse(errMsg, mReader.perClusterTimesAvailable());
+
+ // Verify that a second call won't read the proc file again
+ assertTrue(mTestFile.delete());
+ actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ assertFalse(errMsg, mReader.perClusterTimesAvailable());
+ }
+ }
+
+ @Test
+ public void testReadFreqs_perClusterTimesAvailable() throws Exception {
+ final long[][] freqs = {
+ {1, 12, 123, 1234},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456},
+ {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
+ };
+ final int[] numClusters = {1, 2, 3};
+ final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
+ for (int i = 0; i < freqs.length; ++i) {
+ mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+ writeToFile(freqsLine(freqs[i]));
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+ Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+ assertTrue(errMsg, mReader.perClusterTimesAvailable());
+
+ // Verify that a second call won't read the proc file again
+ assertTrue(mTestFile.delete());
+ actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs[i], actualFreqs);
+ assertTrue(errMsg, mReader.perClusterTimesAvailable());
+ }
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], times[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that readDelta also reads the frequencies if not already available.
+ assertTrue(mTestFile.delete());
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs, actualFreqs);
+
+ // Verify that a second call will only return deltas.
+ mCallback.clear();
+ final long[][] newTimes1 = increaseTime(times);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ mCallback.clear();
+ final long[][] newTimes2 = increaseTime(newTimes1);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+ mReader.readDelta(null);
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ mCallback.clear();
+ final long[][] newTimes3 = increaseTime(newTimes2);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that readDelta also reads the frequencies if not already available.
+ assertTrue(mTestFile.delete());
+ long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+ assertArrayEquals(freqs, actualFreqs);
+
+ // Verify that a second call should still return absolute values
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times2[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaWrongData() throws Exception {
+ final long[] freqs = {110, 123, 145, 167, 289, 997};
+ final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ mCallback.clear();
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] = 1000;
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the internal state was not modified.
+ mCallback.clear();
+ final long[][] times3 = increaseTime(times2);
+ times3[0] = increaseTime(times1)[0];
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times3[i], times2[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that there is no callback if any value in the proc file is -ve.
+ mCallback.clear();
+ final long[][] times4 = increaseTime(times3);
+ times4[0][0] *= -1;
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; ++i) {
+ mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+
+ // Verify that the internal state was not modified when the proc file had -ve value.
+ mCallback.clear();
+ final long[][] times5 = increaseTime(times4);
+ times5[0] = increaseTime(times3)[0];
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+ mReader.readDelta(mCallback);
+ mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
+ }
+
+ assertTrue(mTestFile.delete());
+ }
+
+ private String freqsLine(long[] freqs) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("uid:");
+ for (int i = 0; i < freqs.length; ++i) {
+ sb.append(" " + freqs[i]);
+ }
+ return sb.append('\n').toString();
+ }
+
+ private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
+ assertEquals(numClusters, clusterFreqs.length);
+ when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
+ for (int i = 0; i < numClusters; ++i) {
+ when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
+ }
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j] / 10);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 10 + 10;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
new file mode 100644
index 0000000..9b4512b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidUserSysTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidUserSysTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidUserSysTimeReaderTest {
+ private File mTestDir;
+ private File mTestFile;
+ private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
+ private VerifiableCallback mCallback;
+
+ private Random mRand = new Random(12345);
+ private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+ private final long[][] mInitialTimes = new long[][]{
+ {15334000, 310964000},
+ {537000, 114000},
+ {40000, 10000},
+ {170000, 57000},
+ {5377000, 867000},
+ {47000, 17000}
+ };
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuUidUserSysTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ mCallback = new VerifiableCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(getContext().getFilesDir());
+ }
+
+ @Test
+ public void testThrottler() throws Exception {
+ mReader = new KernelCpuUidUserSysTimeReader(
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
+ mReader.setThrottle(500);
+
+ writeToFile(uidLines(mUids, mInitialTimes));
+ mReader.readDelta(mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ long[][] times1 = increaseTime(mInitialTimes);
+ writeToFile(uidLines(mUids, times1));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(0, mCallback.mData.size());
+
+ SystemClock.sleep(600);
+
+ long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ long[][] times3 = increaseTime(times2);
+ writeToFile(uidLines(mUids, times3));
+ mCallback.clear();
+ mReader.readDelta(mCallback);
+ assertEquals(0, mCallback.mData.size());
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that a second call will only return deltas.
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that there won't be a callback if the proc file values didn't change.
+ mReader.readDelta(mCallback);
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that calling with a null callback doesn't result in any crashes
+ final long[][] times3 = increaseTime(times2);
+ writeToFile(uidLines(mUids, times3));
+ mReader.readDelta(null);
+
+ // Verify that the readDelta call will only return deltas when
+ // the previous call had null callback.
+ final long[][] times4 = increaseTime(times3);
+ writeToFile(uidLines(mUids, times4));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadDeltaWrongData() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readDelta(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that there should not be a callback for a particular UID if its time decreases.
+ final long[][] times2 = increaseTime(times1);
+ times2[0][0] = 1000;
+ writeToFile(uidLines(mUids, times2));
+ mReader.readDelta(mCallback);
+ for (int i = 1; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ @Test
+ public void testReadAbsolute() throws Exception {
+ final long[][] times1 = mInitialTimes;
+ writeToFile(uidLines(mUids, times1));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times1[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+
+ // Verify that a second call should still return absolute values
+ final long[][] times2 = increaseTime(times1);
+ writeToFile(uidLines(mUids, times2));
+ mReader.readAbsolute(mCallback);
+ for (int i = 0; i < mUids.length; i++) {
+ mCallback.verify(mUids[i], times2[i]);
+ }
+ mCallback.verifyNoMoreInteractions();
+ mCallback.clear();
+ assertTrue(mTestFile.delete());
+ }
+
+ private String uidLines(int[] uids, long[][] times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < uids.length; i++) {
+ sb.append(uids[i]).append(':');
+ for (int j = 0; j < times[i].length; j++) {
+ sb.append(' ').append(times[i][j]);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void writeToFile(String s) throws IOException {
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(s);
+ w.flush();
+ }
+ }
+
+ private long[][] increaseTime(long[][] original) {
+ long[][] newTime = new long[original.length][original[0].length];
+ for (int i = 0; i < original.length; i++) {
+ for (int j = 0; j < original[0].length; j++) {
+ newTime[i][j] = original[i][j] + mRand.nextInt(1000) * 1000 + 1000;
+ }
+ }
+ return newTime;
+ }
+
+ private long[] subtract(long[] a1, long[] a2) {
+ long[] val = new long[a1.length];
+ for (int i = 0; i < val.length; ++i) {
+ val[i] = a1[i] - a2[i];
+ }
+ return val;
+ }
+
+ private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+ SparseArray<long[]> mData = new SparseArray<>();
+
+ public void verify(int uid, long[] cpuTimes) {
+ long[] array = mData.get(uid);
+ assertNotNull(array);
+ assertArrayEquals(cpuTimes, array);
+ mData.remove(uid);
+ }
+
+ public void clear() {
+ mData.clear();
+ }
+
+ @Override
+ public void onUidCpuTime(int uid, long[] times) {
+ long[] array = new long[times.length];
+ System.arraycopy(times, 0, array, 0, array.length);
+ mData.put(uid, array);
+ }
+
+ public void verifyNoMoreInteractions() {
+ assertEquals(0, mData.size());
+ }
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index a4c5ed2..141948f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -201,6 +201,24 @@
<new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</split-permission>
+ <!-- Apps holding either the legacy READ or WRITE permissions will inherit
+ the ability to <em>read</em> new typed permissions in the Q release; they
+ won't gain the ability to <em>write</em> that content. -->
+ <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="10000">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+ </split-permission>
+ <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+ <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"
+ targetSdk="10000">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+ </split-permission>
+
<!-- This is a list of all the libraries available for application
code to link against. -->
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 0119a02..dea2f45 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -670,7 +670,7 @@
@Override
public int getAlpha() {
- return PixelFormat.TRANSLUCENT;
+ return mPaint.getAlpha();
}
@Override
@@ -714,10 +714,7 @@
@Override
public int getOpacity() {
- if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
- return mLayerState.mOpacityOverride;
- }
- return mLayerState.getOpacity();
+ return PixelFormat.TRANSLUCENT;
}
@Override
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index f0a3a95..1b5cb60 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -27,6 +27,7 @@
namespace uirenderer {
#define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define SK_MATRIX_STRING_V "[%.9f %.9f %.9f] [%.9f %.9f %.9f] [%.9f %.9f %.9f]"
#define SK_MATRIX_ARGS(m) \
(m)->get(0), (m)->get(1), (m)->get(2), (m)->get(3), (m)->get(4), (m)->get(5), (m)->get(6), \
(m)->get(7), (m)->get(8)
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 13d2dae..0cd6406 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <utils/MathUtils.h>
#include "LayerDrawable.h"
#include "GrBackendSurface.h"
@@ -32,6 +33,24 @@
}
}
+// This is a less-strict matrix.isTranslate() that will still report being translate-only
+// on imperceptibly small scaleX & scaleY values.
+static bool isBasicallyTranslate(const SkMatrix& matrix) {
+ if (!matrix.isScaleTranslate()) return false;
+ return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+}
+
+static bool shouldFilter(const SkMatrix& matrix) {
+ if (!matrix.isScaleTranslate()) return true;
+
+ // We only care about meaningful scale here
+ bool noScale = MathUtils::isOne(matrix.getScaleX())
+ && MathUtils::isOne(matrix.getScaleY());
+ bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
+ && SkScalarIsInt(matrix.getTranslateY());
+ return !(noScale && pixelAligned);
+}
+
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
const SkRect* srcRect, const SkRect* dstRect,
bool useLayerTransform) {
@@ -101,7 +120,7 @@
// Integer translation is defined as when src rect and dst rect align fractionally.
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
// only for SrcOver blending and without color filter (readback uses Src blending).
- bool isIntegerTranslate = totalMatrix.isTranslate()
+ bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
&& SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
== SkScalarFraction(skiaSrcRect.fLeft)
&& SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
@@ -112,10 +131,7 @@
canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
- bool isIntegerTranslate = totalMatrix.isTranslate()
- && SkScalarIsInt(totalMatrix[SkMatrix::kMTransX])
- && SkScalarIsInt(totalMatrix[SkMatrix::kMTransY]);
- if (layer->getForceFilter() || !isIntegerTranslate) {
+ if (layer->getForceFilter() || shouldFilter(totalMatrix)) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
canvas->drawImage(layerImage.get(), 0, 0, &paint);
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 5475898..cc8d83f 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -34,6 +34,10 @@
return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
}
+ inline static bool isOne(float value) {
+ return areEqual(value, 1.0f);
+ }
+
inline static bool isPositive(float value) { return value >= NON_ZERO_EPSILON; }
/**
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 7627cf6..180183e 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -26,7 +26,9 @@
oneway interface ILocationListener
{
void onLocationChanged(in Location location);
- void onStatusChanged(String provider, int status, in Bundle extras);
void onProviderEnabled(String provider);
void onProviderDisabled(String provider);
+
+ // --- deprecated ---
+ void onStatusChanged(String provider, int status, in Bundle extras);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index b5d835a..ff2fad4 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -99,9 +99,10 @@
void clearTestProviderLocation(String provider, String opPackageName);
void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
void clearTestProviderEnabled(String provider, String opPackageName);
+
+ // --- deprecated ---
void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
String opPackageName);
- void clearTestProviderStatus(String provider, String opPackageName);
boolean sendExtraCommand(String provider, String command, inout Bundle extras);
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 88904c8..aa9dddc 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -44,29 +44,12 @@
void onLocationChanged(Location location);
/**
- * Called when the provider status changes. This method is called when
- * a provider is unable to fetch a location or if the provider has recently
- * become available after a period of unavailability.
+ * This callback will never be invoked and providers can be considers as always in the
+ * {@link LocationProvider#AVAILABLE} state.
*
- * @param provider the name of the location provider associated with this
- * update.
- * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
- * provider is out of service, and this is not expected to change in the
- * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
- * the provider is temporarily unavailable but is expected to be available
- * shortly; and {@link LocationProvider#AVAILABLE} if the
- * provider is currently available.
- * @param extras an optional Bundle which will contain provider specific
- * status variables.
- *
- * <p> A number of common key/value pairs for the extras Bundle are listed
- * below. Providers that use any of the keys on this list must
- * provide the corresponding value as described below.
- *
- * <ul>
- * <li> satellites - the number of satellites used to derive the fix
- * </ul>
+ * @deprecated This callback will never be invoked.
*/
+ @Deprecated
void onStatusChanged(String provider, int status, Bundle extras);
/**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 02680ab..b66ceef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -146,9 +146,14 @@
public static final String KEY_PROXIMITY_ENTERING = "entering";
/**
+ * This key is no longer in use.
+ *
* Key used for a Bundle extra holding an Integer status value
* when a status change is broadcast using a PendingIntent.
+ *
+ * @deprecated Status changes are deprecated and no longer broadcast.
*/
+ @Deprecated
public static final String KEY_STATUS_CHANGED = "status";
/**
@@ -1581,8 +1586,7 @@
}
/**
- * Sets mock status values for the given provider. These values will be used in place
- * of any actual values from the provider.
+ * This method has no effect as provider status has been deprecated and is no longer supported.
*
* @param provider the provider name
* @param status the mock status
@@ -1593,7 +1597,10 @@
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This method has no effect.
*/
+ @Deprecated
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
try {
mService.setTestProviderStatus(provider, status, extras, updateTime,
@@ -1604,21 +1611,19 @@
}
/**
- * Removes any mock status values associated with the given provider.
+ * This method has no effect as provider status has been deprecated and is no longer supported.
*
* @param provider the provider name
- *
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * @deprecated This method has no effect.
*/
+ @Deprecated
public void clearTestProviderStatus(String provider) {
- try {
- mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L);
}
// --- GPS-specific support ---
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index c4fd097..b69a9d7 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -34,8 +34,23 @@
* user-specified criteria.
*/
public class LocationProvider {
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int OUT_OF_SERVICE = 0;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int TEMPORARILY_UNAVAILABLE = 1;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
public static final int AVAILABLE = 2;
/**
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 1e69f16..d19559e 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,8 +11,8 @@
method public abstract void onDisable();
method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public abstract void onEnable();
- method public abstract int onGetStatus(android.os.Bundle);
- method public abstract long onGetStatusUpdateTime();
+ method public deprecated int onGetStatus(android.os.Bundle);
+ method public deprecated long onGetStatusUpdateTime();
method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
method public final void reportLocation(android.location.Location);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 30655f5..d45a4ba 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,14 +16,11 @@
package com.android.location.provider;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
+import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +33,10 @@
import com.android.internal.location.ProviderRequest;
import com.android.internal.util.FastPrintWriter;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+
/**
* Base class for location providers implemented as unbundled services.
*
@@ -173,6 +174,8 @@
}
/**
+ * This method will no longer be invoked.
+ *
* Returns a information on the status of this provider.
* <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
* out of service, and this is not expected to change in the near
@@ -183,10 +186,17 @@
*
* <p>If extras is non-null, additional status information may be
* added to it in the form of provider-specific key/value pairs.
+ *
+ * @deprecated This method will no longer be invoked.
*/
- public abstract int onGetStatus(Bundle extras);
+ @Deprecated
+ public int onGetStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
/**
+ * This method will no longer be invoked.
+ *
* Returns the time at which the status was last updated. It is the
* responsibility of the provider to appropriately set this value using
* {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
@@ -195,8 +205,13 @@
* the same status again.
*
* @return time of last status update in millis since last reboot
+ *
+ * @deprecated This method will no longer be invoked.
*/
- public abstract long onGetStatusUpdateTime();
+ @Deprecated
+ public long onGetStatusUpdateTime() {
+ return 0;
+ }
/**
* Implements addditional location provider specific additional commands.
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 976d380..ff1bdd4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1911,7 +1911,7 @@
* system failed to generate a new session, a condition in which audio playback or recording
* will subsequently fail as well.
*/
- public static int generateAudioSessionId() {
+ public int generateAudioSessionId() {
int session = AudioSystem.newAudioSessionId();
if (session > 0) {
return session;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 995ebb2..dfe29e9 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1135,6 +1135,10 @@
maxChannels = 6;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
maxChannels = 16;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
+ sampleRates = new int[] { 44100, 48000, 96000, 192000 };
+ bitRates = Range.create(16000, 2688000);
+ maxChannels = 24;
} else {
Log.w(TAG, "Unsupported mime " + mime);
mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index c203fa9..7785900 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -434,9 +434,12 @@
*/
@NonNull
public List<AudioPresentation> getAudioPresentations(int trackIndex) {
- return new ArrayList<AudioPresentation>();
+ return native_getAudioPresentations(trackIndex);
}
+ @NonNull
+ private native List<AudioPresentation> native_getAudioPresentations(int trackIndex);
+
/**
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5dee16e..284e422 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -138,6 +138,7 @@
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+ public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
/**
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index 5306de6..a3adddd 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -14,173 +14,135 @@
* limitations under the License.
*/
-#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
-#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_
#include "jni.h"
-#include <media/AudioPresentationInfo.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-
+#include <media/stagefright/foundation/ADebug.h> // CHECK
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <nativehelper/ScopedLocalRef.h>
namespace android {
struct JAudioPresentationInfo {
struct fields_t {
- jclass clazz;
+ jclass clazz = NULL;
jmethodID constructID;
// list parameters
- jclass listclazz;
+ jclass listClazz = NULL;
jmethodID listConstructId;
jmethodID listAddId;
+ // hashmap parameters
+ jclass hashMapClazz = NULL;
+ jmethodID hashMapConstructID;
+ jmethodID hashMapPutID;
+
+ // ulocale parameters
+ jclass ulocaleClazz = NULL;
+ jmethodID ulocaleConstructID;
+
void init(JNIEnv *env) {
jclass lclazz = env->FindClass("android/media/AudioPresentation");
- if (lclazz == NULL) {
- return;
- }
-
+ CHECK(lclazz != NULL);
clazz = (jclass)env->NewGlobalRef(lclazz);
- if (clazz == NULL) {
- return;
- }
-
+ CHECK(clazz != NULL);
constructID = env->GetMethodID(clazz, "<init>",
- "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
- env->DeleteLocalRef(lclazz);
+ "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
+ CHECK(constructID != NULL);
// list objects
- jclass llistclazz = env->FindClass("java/util/ArrayList");
- CHECK(llistclazz != NULL);
- listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
- CHECK(listclazz != NULL);
- listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+ jclass llistClazz = env->FindClass("java/util/ArrayList");
+ CHECK(llistClazz != NULL);
+ listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
+ CHECK(listClazz != NULL);
+ listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
CHECK(listConstructId != NULL);
- listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+ listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
CHECK(listAddId != NULL);
- env->DeleteLocalRef(llistclazz);
+
+ // hashmap objects
+ jclass lhashMapClazz = env->FindClass("java/util/HashMap");
+ CHECK(lhashMapClazz != NULL);
+ hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
+ CHECK(hashMapClazz != NULL);
+ hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
+ CHECK(hashMapConstructID != NULL);
+ hashMapPutID = env->GetMethodID(
+ hashMapClazz,
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ CHECK(hashMapPutID != NULL);
+
+ jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
+ CHECK(lulocaleClazz != NULL);
+ ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
+ CHECK(ulocaleClazz != NULL);
+ ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
+ CHECK(ulocaleConstructID != NULL);
}
void exit(JNIEnv *env) {
- env->DeleteGlobalRef(clazz);
- clazz = NULL;
- env->DeleteGlobalRef(listclazz);
- listclazz = NULL;
+ env->DeleteGlobalRef(clazz); clazz = NULL;
+ env->DeleteGlobalRef(listClazz); listClazz = NULL;
+ env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
+ env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
}
};
- static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
- ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
-
- if (hashMapClazz.get() == NULL) {
- return -EINVAL;
- }
- jmethodID hashMapConstructID =
- env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
- if (hashMapConstructID == NULL) {
- return -EINVAL;
- }
- jmethodID hashMapPutID =
- env->GetMethodID(
- hashMapClazz.get(),
- "put",
- "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
- if (hashMapPutID == NULL) {
- return -EINVAL;
- }
-
- jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
- for (size_t i = 0; i < msg->countEntries(); ++i) {
- AMessage::Type valueType;
- const char *key = msg->getEntryNameAt(i, &valueType);
-
- if (!strncmp(key, "android._", 9)) {
- // don't expose private keys (starting with android._)
- continue;
- }
- jobject valueObj = NULL;
- AString val;
- CHECK(msg->findString(key, &val));
- valueObj = env->NewStringUTF(val.c_str());
- if (valueObj != NULL) {
- ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
- if (localeClazz.get() == NULL) {
- return -EINVAL;
- }
- jmethodID localeConstructID =
- env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
- if (localeConstructID == NULL) {
- return -EINVAL;
- }
- jstring jLanguage = env->NewStringUTF(key);
- jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
- env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
- env->DeleteLocalRef(jLocale); jLocale = NULL;
- env->DeleteLocalRef(valueObj); valueObj = NULL;
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
- }
- }
-
- *map = hashMap;
-
- return OK;
+ static jobject asJobject(JNIEnv *env, const fields_t& fields) {
+ return env->NewObject(fields.listClazz, fields.listConstructId);
}
- jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
- jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
-
- for (size_t i = 0; i < info.countPresentations(); ++i) {
- const sp<AudioPresentation> &ap = info.getPresentation(i);
- jobject jLabelObject;
-
- sp<AMessage> labelMessage = new AMessage();
- for (size_t i = 0; i < ap->mLabels.size(); ++i) {
- labelMessage->setString(ap->mLabels.keyAt(i).string(),
- ap->mLabels.valueAt(i).string());
+ static void addPresentations(JNIEnv *env, const fields_t& fields,
+ const AudioPresentationCollection& presentations, jobject presentationsJObj) {
+ for (const auto& ap : presentations) {
+ ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
+ if (jLabelObject == nullptr) return;
+ ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
+ if (jLanguage == nullptr) return;
+ ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+ fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
+ ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
+ static_cast<jint>(ap.mPresentationId),
+ static_cast<jint>(ap.mProgramId),
+ jLocale.get(),
+ static_cast<jint>(ap.mMasteringIndication),
+ static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
+ static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
+ static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
+ jLabelObject.get()));
+ if (jValueObj != nullptr) {
+ env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
}
- if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
- return NULL;
- }
- ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
- if (localeClazz.get() == NULL) {
- return NULL;
- }
- jmethodID localeConstructID =
- env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
- if (localeConstructID == NULL) {
- return NULL;
- }
- jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
- jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
- jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
- static_cast<jint>(ap->mPresentationId),
- static_cast<jint>(ap->mProgramId),
- jLocale,
- static_cast<jint>(ap->mMasteringIndication),
- static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
- 1 : 0),
- static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
- 1 : 0),
- static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
- 1 : 0),
- jLabelObject);
- if (jValueObj == NULL) {
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
- return NULL;
- }
-
- env->CallBooleanMethod(list, fields.listAddId, jValueObj);
- env->DeleteLocalRef(jLocale); jLocale = NULL;
- env->DeleteLocalRef(jValueObj); jValueObj = NULL;
- env->DeleteLocalRef(jLanguage); jLanguage = NULL;
}
- return list;
+ }
+
+ private:
+ static ScopedLocalRef<jobject> convertLabelsToMap(
+ JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
+ ScopedLocalRef<jobject> nullMap(env, nullptr);
+ ScopedLocalRef<jobject> hashMap(env, env->NewObject(
+ fields.hashMapClazz, fields.hashMapConstructID));
+ if (hashMap == nullptr) {
+ return nullMap;
+ }
+
+ for (const auto& label : labels) {
+ ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
+ if (jLanguage == nullptr) return nullMap;
+ ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+ fields.ulocaleClazz,
+ fields.ulocaleConstructID,
+ jLanguage.get()));
+ if (jLocale == nullptr) return nullMap;
+ ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
+ if (jValue == nullptr) return nullMap;
+ env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
+ }
+ return hashMap;
}
};
} // namespace android
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 15957c6..29238d3 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "MediaExtractor-JNI"
#include <utils/Log.h>
+#include "android_media_AudioPresentation.h"
#include "android_media_MediaDataSource.h"
#include "android_media_MediaExtractor.h"
#include "android_media_MediaMetricsJNI.h"
@@ -56,6 +57,7 @@
};
static fields_t gFields;
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
: mClass(NULL),
@@ -289,6 +291,10 @@
return mImpl->getCachedDuration(durationUs, eos);
}
+status_t JMediaExtractor::getAudioPresentations(size_t trackIdx,
+ AudioPresentationCollection *presentations) const {
+ return mImpl->getAudioPresentations(trackIdx, presentations);
+}
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -668,6 +674,28 @@
return JNI_TRUE;
}
+static jobject android_media_MediaExtractor_getAudioPresentations(
+ JNIEnv *env, jobject thiz, jint trackIdx) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+ jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return presentationsJObj;
+ }
+ AudioPresentationCollection presentations;
+ status_t err = extractor->getAudioPresentations(trackIdx, &presentations);
+ if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) {
+ return presentationsJObj;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return presentationsJObj;
+ }
+
+ JAudioPresentationInfo::addPresentations(
+ env, gAudioPresentationFields, presentations, presentationsJObj);
+ return presentationsJObj;
+}
+
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/MediaExtractor");
CHECK(clazz != NULL);
@@ -683,6 +711,8 @@
gFields.cryptoInfoSetPatternID =
env->GetMethodID(clazz, "setPattern", "(II)V");
+
+ gAudioPresentationFields.init(env);
}
static void android_media_MediaExtractor_native_setup(
@@ -963,6 +993,9 @@
{"native_getMetrics", "()Landroid/os/PersistableBundle;",
(void *)android_media_MediaExtractor_native_getMetrics},
+
+ { "native_getAudioPresentations", "(I)Ljava/util/List;",
+ (void *)android_media_MediaExtractor_getAudioPresentations },
};
int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index aaa8421..baa779c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -18,6 +18,7 @@
#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <media/MediaSource.h>
#include <media/DataSource.h>
#include <utils/Errors.h>
@@ -66,6 +67,8 @@
status_t getMetrics(Parcel *reply) const;
bool getCachedDuration(int64_t *durationUs, bool *eos) const;
+ status_t getAudioPresentations(size_t trackIdx,
+ AudioPresentationCollection *presentations) const;
protected:
virtual ~JMediaExtractor();
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index 7cb49da..e3e52b1 100644
--- a/native/webview/plat_support/draw_gl_functor.cpp
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -42,10 +42,10 @@
class DrawGLFunctor : public Functor {
public:
explicit DrawGLFunctor(jlong view_context) : view_context_(view_context) {}
- virtual ~DrawGLFunctor() {}
+ ~DrawGLFunctor() override {}
// Functor
- virtual status_t operator ()(int what, void* data) {
+ status_t operator ()(int what, void* data) override {
using uirenderer::DrawGlInfo;
if (!g_aw_drawgl_function) {
ALOGE("Cannot draw: no DrawGL Function installed");
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 8f13497..f244f9f 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -45,6 +45,7 @@
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
+ "car-theme-lib-bp",
"SystemUI-tags",
"SystemUI-proto",
],
diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
index c98740e..709797d 100644
--- a/packages/CarSystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -20,11 +20,9 @@
android:id="@+id/volume_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@android:color/black"
android:minWidth="@dimen/volume_dialog_panel_width"
- android:theme="@style/Theme.Car.DialogListView"
- app:dividerEndMargin="@dimen/car_keyline_1"
- app:dividerStartMargin="@dimen/car_keyline_1"
+ android:theme="@style/PagedListViewTheme"
app:gutter="none"
app:scrollBarEnabled="false"
+ app:listDividerColor="@color/list_divider_color"
app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index df8f8db..c510ab6 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -51,4 +51,6 @@
<color name="car_grey_900">#ff212121</color>
<color name="keyguard_button_text_color">@android:color/black</color>
+
+ <color name="list_divider_color">@*android:color/car_list_divider_light</color>
</resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7f4544a..0d95d30 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -41,15 +41,16 @@
<item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
</style>
- <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
- <item name="android:colorControlActivated">@color/car_accent</item>
- <item name="listItemBackgroundColor">@android:color/black</item>
- </style>
-
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
<item name="android:background">@drawable/nav_button_background</item>
</style>
-</resources>
+ <style name="PagedListViewTheme" parent="@style/Theme.CarSupportWrapper.NoActionBar">
+ <item name="android:background">@*android:color/car_background</item>
+ <item name="listItemBackgroundColor">@*android:color/car_background</item>
+ <item name="dividerEndMargin">@dimen/car_keyline_1</item>
+ <item name="dividerStartMargin">@dimen/car_keyline_1</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 60153fcb..22d0e3b 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -355,6 +355,29 @@
}
@Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ if (DEBUG) {
+ Log.i(TAG,
+ "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
+ + ", isExpanded = isExpanded");
+ }
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+ }
+
+ @Override
+ public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+ if (DEBUG) {
+ Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
+ + "], source = [" + source + "]");
+ }
+ }
+
+ @Override
public void onListenerConnected() {
if (DEBUG) Log.i(TAG, "CONNECTED");
try {
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 56feb47..87d6e4a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -16,27 +16,24 @@
package com.android.location.fused;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.ProviderPropertiesUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
-import android.location.LocationProvider;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.ProviderPropertiesUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
private static final String TAG = "FusedLocationProvider";
@@ -48,7 +45,6 @@
private static final int MSG_DISABLE = 2;
private static final int MSG_SET_REQUEST = 3;
- private final Context mContext;
private final FusionEngine mEngine;
private static class RequestWrapper {
@@ -62,13 +58,12 @@
public FusedLocationProvider(Context context) {
super(TAG, PROPERTIES);
- mContext = context;
mEngine = new FusionEngine(context, Looper.myLooper());
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ context.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -122,14 +117,4 @@
// perform synchronously
mEngine.dump(fd, pw, args);
}
-
- @Override
- public int onGetStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- @Override
- public long onGetStatusUpdateTime() {
- return 0;
- }
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d1f140f..444e724 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,6 +16,7 @@
"SettingsLibAppPreference",
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
+ "SettingsLayoutPreference",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
new file mode 100644
index 0000000..489d360
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLayoutPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
new file mode 100644
index 0000000..ee4ce49
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
new file mode 100644
index 0000000..0678263
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/entity_header"
+ style="@style/EntityHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitXY"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginTop="8dp"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"/>
+
+ <TextView
+ android:id="@+id/entity_header_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"/>
+
+ <TextView
+ android:id="@+id/entity_header_second_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/entity_header_links"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@android:id/button1"
+ style="?android:attr/actionOverflowButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="0dp"
+ android:minWidth="24dp"
+ android:src="@null"
+ android:tint="?android:attr/colorAccent"/>
+
+ <ImageButton
+ android:id="@android:id/button2"
+ style="?android:attr/actionOverflowButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="0dp"
+ android:minWidth="24dp"
+ android:src="@null"
+ android:tint="?android:attr/colorAccent"/>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
new file mode 100644
index 0000000..805744b
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <style name="EntityHeader">
+ <item name="android:background">?android:attr/colorPrimaryDark</item>
+ <item name="android:paddingTop">24dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ <item name="android:paddingEnd">16dp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderTitle"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">20sp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderSummary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:gravity">start</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
new file mode 100644
index 0000000..2a635b0
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference can be simply customized a view by adding layout attribute in xml.
+ * User also can decide whether or not LayoutPreference allows above divider or below divider.
+ *
+ * For instances,
+ *
+ * <com.android.settingslib.widget.LayoutPreference
+ * ...
+ * android:layout="@layout/settings_entity_header"
+ * xxxxxxx:allowDividerAbove="true"
+ * xxxxxxx:allowDividerBelow="true"
+ *
+ */
+public class LayoutPreference extends Preference {
+
+ private final View.OnClickListener mClickListener = v -> performClick(v);
+ private boolean mAllowDividerAbove;
+ private boolean mAllowDividerBelow;
+ private View mRootView;
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and the supplied
+ * attribute set.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ public LayoutPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme, the supplied
+ * attribute set, and default style attribute.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies default
+ * values for the view. Can be 0 to not look for
+ * defaults.
+ */
+ public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs, defStyleAttr);
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and a customized view id.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param resource The view id which you expected to be inflated and show in preference.
+ */
+ public LayoutPreference(Context context, int resource) {
+ this(context, LayoutInflater.from(context).inflate(resource, null, false));
+ }
+
+ /**
+ * Constructs a new LayoutPreference with the given context's theme and a customized view.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param view The view which you expected show in preference.
+ */
+ public LayoutPreference(Context context, View view) {
+ super(context);
+ setView(view);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
+ mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
+ R.styleable.Preference_allowDividerAbove, false);
+ mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
+ R.styleable.Preference_allowDividerBelow, false);
+ a.recycle();
+
+ a = context.obtainStyledAttributes(
+ attrs, R.styleable.Preference, defStyleAttr, 0);
+ int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0);
+ if (layoutResource == 0) {
+ throw new IllegalArgumentException("LayoutPreference requires a layout to be defined");
+ }
+ a.recycle();
+
+ // Need to create view now so that findViewById can be called immediately.
+ final View view = LayoutInflater.from(getContext())
+ .inflate(layoutResource, null, false);
+ setView(view);
+ }
+
+ private void setView(View view) {
+ setLayoutResource(R.layout.layout_preference_frame);
+ mRootView = view;
+ setShouldDisableView(false);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ holder.itemView.setOnClickListener(mClickListener);
+
+ final boolean selectable = isSelectable();
+ holder.itemView.setFocusable(selectable);
+ holder.itemView.setClickable(selectable);
+ holder.setDividerAllowedAbove(mAllowDividerAbove);
+ holder.setDividerAllowedBelow(mAllowDividerBelow);
+
+ FrameLayout layout = (FrameLayout) holder.itemView;
+ layout.removeAllViews();
+ ViewGroup parent = (ViewGroup) mRootView.getParent();
+ if (parent != null) {
+ parent.removeView(mRootView);
+ }
+ layout.addView(mRootView);
+ }
+
+ /**
+ * Finds the view with the given ID.
+ *
+ * @param id the ID to search for
+ * @return a view with given ID if found, or {@code null} otherwise
+ */
+ public <T extends View> T findViewById(int id) {
+ return mRootView.findViewById(id);
+ }
+
+ /**
+ * LayoutPreference whether or not allows to set a below divider.
+ */
+ public void setAllowDividerBelow(boolean allowed) {
+ mAllowDividerBelow = allowed;
+ }
+
+ /**
+ * Return a value whether or not LayoutPreference allows to set a below divider.
+ */
+ public boolean isAllowDividerBelow() {
+ return mAllowDividerBelow;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 86928fc..4f81daf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -154,8 +154,10 @@
static final String KEY_CARRIER_NAME = "key_carrier_name";
static final AtomicInteger sLastId = new AtomicInteger(0);
- /**
- * These values are matched in string arrays -- changes must be kept in sync
+ /*
+ * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState,
+ * and sent across IPC. The numeric values should remain stable, otherwise the changes will need
+ * to be synced with other unbundled users of this library.
*/
public static final int SECURITY_NONE = 0;
public static final int SECURITY_WEP = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index ede248b..8757eed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -55,6 +55,8 @@
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
+ paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/"
+ + "SettingsLayoutPreference/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
paths.add(resourcePath("file:frameworks/base/core/res/res"));
paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
new file mode 100644
index 0000000..427a611
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class LayoutPreferenceTest {
+
+ private LayoutPreference mPreference;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ final Context mContext = RuntimeEnvironment.application;
+ mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+ mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
+ .inflate(R.layout.layout_preference_frame, null, false));
+ }
+
+ @Test
+ public void setOnPreferenceClickListener_layoutPreferenceShouldListenClickEvent() {
+ final OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
+
+ mPreference.setOnPreferenceClickListener(listener);
+ mPreference.onBindViewHolder(mHolder);
+
+ mHolder.itemView.callOnClick();
+
+ verify(listener).onPreferenceClick(mPreference);
+ assertThat(mHolder.itemView.isFocusable()).isTrue();
+ assertThat(mHolder.itemView.isClickable()).isTrue();
+ }
+
+ @Test
+ public void setNonSelectable_viewShouldNotBeSelectable() {
+ mPreference.setSelectable(false);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.itemView.isFocusable()).isFalse();
+ assertThat(mHolder.itemView.isClickable()).isFalse();
+ }
+
+ @Test
+ public void disableSomeView_shouldMaintainStateAfterBind() {
+ mPreference.findViewById(android.R.id.button1).setEnabled(false);
+ mPreference.findViewById(android.R.id.button2).setEnabled(true);
+
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mPreference.findViewById(android.R.id.button1).isEnabled()).isFalse();
+ assertThat(mPreference.findViewById(android.R.id.button2).isEnabled()).isTrue();
+ }
+
+ @Test
+ public void allowDividerBelow_shouldSaveCorrectDividerStatus() {
+ mPreference.setAllowDividerBelow(true);
+
+ assertThat(mPreference.isAllowDividerBelow()).isTrue();
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index ba4eb5f..88b8dd8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -52,7 +52,9 @@
}
class Sensor {
- public static int TYPE_WAKE_LOCK_SCREEN = 1;
+ public static final int TYPE_WAKE_LOCK_SCREEN = 1;
+ public static final int TYPE_WAKE_DISPLAY = 2;
+ public static final int TYPE_SWIPE = 3;
int mType;
@@ -68,6 +70,7 @@
class TriggerEvent {
Sensor mSensor;
int mVendorType;
+ float[] mValues;
/**
* Creates a trigger event
@@ -76,14 +79,30 @@
* e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
*/
public TriggerEvent(Sensor sensor, int vendorType) {
+ this(sensor, vendorType, null);
+ }
+
+ /**
+ * Creates a trigger event
+ * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
+ * @param vendorType The vendor type, which should be unique for each type of sensor,
+ * e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
+ * @param values Values captured by the sensor.
+ */
+ public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
mSensor = sensor;
mVendorType = vendorType;
+ mValues = values;
}
public Sensor getSensor() {
return mSensor;
}
+ public float[] getValues() {
+ return mValues;
+ }
+
public int getVendorType() {
return mVendorType;
}
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index c86ebe7..eb3f70a 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -51,7 +51,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgicon" />
<TextView
- android:id="@+id/pkg_group_divider"
+ android:id="@+id/pkg_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -61,7 +61,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgname" />
<TextView
- android:id="@+id/group_name"
+ android:id="@+id/delegate_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -70,7 +70,7 @@
android:ellipsize="end"
android:maxLines="1"
android:layout_centerVertical="true"
- android:layout_toEndOf="@id/pkg_group_divider" />
+ android:layout_toEndOf="@id/pkg_divider" />
<!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
<ImageButton
android:id="@+id/info"
@@ -101,13 +101,39 @@
android:layout_marginStart="@*android:dimen/notification_content_margin_start"
android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
android:orientation="vertical">
- <!-- Channel Name -->
- <TextView
- android:id="@+id/channel_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- style="@android:style/TextAppearance.Material.Notification.Title" />
+ <RelativeLayout
+ android:id="@+id/names"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_centerVertical="true" />
+ <TextView
+ android:id="@+id/pkg_group_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:text="@*android:string/notification_header_divider_symbol"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/group_name" />
+ <!-- Channel Name -->
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@android:style/TextAppearance.Material.Notification.Title"
+ android:layout_toEndOf="@id/pkg_group_divider"/>
+ </RelativeLayout>
<!-- Question prompt -->
<TextView
android:id="@+id/block_prompt"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a9d0f68..c5654f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1574,6 +1574,9 @@
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
+ <!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
+ <string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
<string name="appops_camera">This app is using the camera.</string>
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
@@ -2251,7 +2254,10 @@
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
<!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
- <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+ <plurals name="ongoing_privacy_chip_multiple_apps">
+ <item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+ </plurals>
<!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
<string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
@@ -2260,12 +2266,15 @@
<string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
<!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
- <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</string>
+ <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+ <item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+ <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+ </plurals>
<!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
<string name="ongoing_privacy_dialog_cancel">Cancel</string>
- <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+ <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
<string name="ongoing_privacy_dialog_open_settings">View details</string>
<!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6b0a7a9..b55aa5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1531,10 +1531,11 @@
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
- if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
+ boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+ || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+ if (runningOrRestarting && !shouldListenForFingerprint) {
stopListeningForFingerprint();
- } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
- && shouldListenForFingerprint) {
+ } else if (!runningOrRestarting && shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
@@ -1589,6 +1590,10 @@
setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ // Waiting for restart via handleFingerprintError().
+ return;
+ }
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = ActivityManager.getCurrentUser();
if (isUnlockWithFingerprintPossible(userId)) {
@@ -2418,6 +2423,8 @@
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
pw.println(" possible=" + isUnlockWithFingerprintPossible(userId));
+ pw.println(" listening: actual=" + mFingerprintRunningState
+ + " expected=" + (shouldListenForFingerprint() ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e868f96..c6dcfc7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -268,7 +268,7 @@
for (BubbleView bv : mBubbles.values()) {
NotificationData.Entry entry = bv.getEntry();
if (entry != null) {
- if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+ if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
viewsToRemove.add(bv);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index a79e047..6c47aac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -120,7 +120,7 @@
* @return the view to display when the bubble is expanded.
*/
public ExpandableNotificationRow getRowView() {
- return mEntry.row;
+ return mEntry.getRow();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 21b21d9..eda3c59 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -177,9 +177,22 @@
log("state " + state);
}
- public static void traceWakeLockScreenWakeUp() {
+ /**
+ * Appends lock screen wake up event to the logs.
+ * @param wake if we're waking up or sleeping.
+ */
+ public static void traceLockScreenWakeUp(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp");
+ log("wakeLockScreenWakeUp " + wake);
+ }
+
+ /**
+ * Appends wake-display event to the logs.
+ * @param wake if we're waking up or sleeping.
+ */
+ public static void traceWakeDisplay(boolean wake) {
+ if (!ENABLED) return;
+ log("wakeLockScreenWakeUp " + wake);
}
public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 77f7ad4f..bf8e04d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,7 @@
package com.android.systemui.doze;
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
import android.annotation.AnyThread;
@@ -67,7 +68,6 @@
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
- private final Consumer<Boolean> mWakeScreenCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
@@ -76,8 +76,7 @@
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
- Callback callback, Consumer<Boolean> proxCallback,
- Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
+ Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -85,7 +84,6 @@
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
- mWakeScreenCallback = wakeScreenCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -123,7 +121,13 @@
DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */),
- new WakeScreenSensor(),
+ new PluginTriggerSensor(
+ new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
+ Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
+ true /* configured */,
+ DozeLog.REASON_SENSOR_WAKE_UP,
+ false /* reports touch coordinates */,
+ false /* touchscreen */),
};
mProxSensor = new ProxSensor(policy);
@@ -395,7 +399,8 @@
screenX = event.values[0];
screenY = event.values[1];
}
- mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY);
+ mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
+ event.values);
updateListener(); // reregister, this sensor only fires once
}));
}
@@ -429,7 +434,14 @@
private final SensorManagerPlugin.Sensor mPluginSensor;
private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
- onTrigger(null);
+ DozeLog.traceSensor(mContext, mPulseReason);
+ mHandler.post(mWakeLock.wrap(() -> {
+ if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+ mRegistered = false;
+ mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+ event.getValues());
+ updateListener(); // reregister, this sensor only fires once
+ }));
};
PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
@@ -463,28 +475,19 @@
.append(", mSensor=").append(mPluginSensor).append("}").toString();
}
- }
-
- private class WakeScreenSensor extends TriggerSensor {
-
- WakeScreenSensor() {
- super(findSensorWithType(mConfig.wakeScreenSensorType()),
- Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
- DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
- false /* requiresTouchscreen */);
+ private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+ if (event == null) return null;
+ final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
+ .append(event.getSensor()).append(',')
+ .append(event.getVendorType());
+ if (event.getValues() != null) {
+ for (int i = 0; i < event.getValues().length; i++) {
+ sb.append(',').append(event.getValues()[i]);
+ }
+ }
+ return sb.append(']').toString();
}
- @Override
- @AnyThread
- public void onTrigger(TriggerEvent event) {
- DozeLog.traceSensor(mContext, mPulseReason);
- mHandler.post(mWakeLock.wrap(() -> {
- if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
- mRegistered = false;
- mWakeScreenCallback.accept(event.values[0] > 0);
- updateListener(); // reregister, this sensor only fires once
- }));
- }
}
public interface Callback {
@@ -494,11 +497,11 @@
* @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
* @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
* @param screenX the location on the screen where the sensor fired or -1
- * if the sensor doesn't support reporting screen locations.
+ * if the sensor doesn't support reporting screen locations.
* @param screenY the location on the screen where the sensor fired or -1
- * if the sensor doesn't support reporting screen locations.
+ * @param rawValues raw values array from the event.
*/
void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck,
- float screenX, float screenY);
+ float screenX, float screenY, float[] rawValues);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index d69b1bf..bad0148 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
+ config, wakeLock, this::onSensor, this::onProximityFar,
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -124,13 +124,17 @@
}
private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
- float screenX, float screenY) {
+ float screenX, float screenY, float[] rawValues) {
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+ boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
- if (isLongPress) {
+ if (isWakeDisplay) {
+ onWakeScreen(wakeEvent);
+ } else if (isLongPress) {
requestPulse(pulseReason, sensorPerformedProxCheck);
} else {
proximityCheckThenCall((result) -> {
@@ -141,7 +145,15 @@
if (isDoubleTap) {
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
- } else if (isPickup || isWakeLockScreen) {
+ } else if (isWakeLockScreen) {
+ if (wakeEvent) {
+ mDozeHost.setPassiveInterrupt(true);
+ mMachine.wakeUp();
+ DozeLog.traceLockScreenWakeUp(wakeEvent);
+ } else {
+ if (DEBUG) Log.d(TAG, "Unpulsing");
+ }
+ } else if (isPickup) {
mDozeHost.setPassiveInterrupt(true);
mMachine.wakeUp();
} else {
@@ -157,8 +169,6 @@
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
- } else if (isWakeLockScreen) {
- DozeLog.traceWakeLockScreenWakeUp();
}
}
@@ -184,6 +194,7 @@
}
private void onWakeScreen(boolean wake) {
+ DozeLog.traceWakeDisplay(wake);
DozeMachine.State state = mMachine.getState();
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e78951a..201c7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -175,7 +174,7 @@
* @param builder The slice builder.
*/
protected void addZenMode(ListBuilder builder) {
- if (!isDndSuppressingNotifications()) {
+ if (!isDndOn()) {
return;
}
RowBuilder dndBuilder = new RowBuilder(mDndUri)
@@ -187,13 +186,10 @@
}
/**
- * Return true if DND is enabled suppressing notifications.
+ * Return true if DND is enabled.
*/
- protected boolean isDndSuppressingNotifications() {
- boolean suppressingNotifications = (mZenModeController.getConfig().suppressedVisualEffects
- & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
- return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF
- && suppressingNotifications;
+ protected boolean isDndOn() {
+ return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 5d33ffd..f054345 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -411,13 +411,8 @@
setNextLogTime();
- // This initialization method may be called on a configuration change. Only one set of
- // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
- // schedule an ongoing callback.
- mHandler.removeCallbacks(mUpdateTempCallback);
-
// We have passed all of the checks, start checking the temp
- updateTemperatureWarning();
+ mHandler.post(mUpdateTempCallback);
}
private void showThermalShutdownDialog() {
@@ -448,6 +443,8 @@
logTemperatureStats();
+ // Remove any pending callbacks as we only want to enable one
+ mHandler.removeCallbacks(mUpdateTempCallback);
mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
}
@@ -553,11 +550,7 @@
// Thermal event received from vendor thermal management subsystem
private final class ThermalEventListener extends IThermalEventListener.Stub {
@Override public void notifyThrottling(Temperature temp) {
- // Trigger an update of the temperature warning. Only one
- // callback can be enabled at a time, so remove any existing
- // callback; updateTemperatureWarning will schedule another one.
- mHandler.removeCallbacks(mUpdateTempCallback);
- updateTemperatureWarning();
+ mHandler.post(mUpdateTempCallback);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index d3715d0..65ed889 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -78,8 +78,9 @@
if (builder.app != null) {
text.setText(builder.app?.applicationName)
} else {
- text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
- builder.appsAndTypes.size)
+ text.text = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_chip_multiple_apps,
+ builder.appsAndTypes.size, builder.appsAndTypes.size)
}
}
} else {
@@ -100,9 +101,9 @@
context.getString(R.string.ongoing_privacy_chip_content_single_app,
builder.app?.applicationName, typesText)
} else {
- contentDescription = context.getString(
- R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
- builder.appsAndTypes.size, typesText)
+ contentDescription = context.resources.getQuantityString(
+ R.plurals.ongoing_privacy_chip_content_multiple_apps_single_op,
+ builder.appsAndTypes.size, builder.appsAndTypes.size, typesText)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 3fa3e8e..268462e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -18,10 +18,14 @@
import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dependency
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
@@ -33,25 +37,29 @@
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
+ val intents = listOf(Intent.ACTION_USER_FOREGROUND,
+ Intent.ACTION_MANAGED_PROFILE_ADDED,
+ Intent.ACTION_MANAGED_PROFILE_REMOVED)
+ const val TAG = "PrivacyItemController"
}
private var privacyList = emptyList<PrivacyItem>()
private val appOpsController = Dependency.get(AppOpsController::class.java)
private val userManager = context.getSystemService(UserManager::class.java)
- private val currentUser = ActivityManager.getCurrentUser()
- private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ private var currentUserIds = emptyList<Int>()
private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
+ private var listening = false
+
private val notifyChanges = Runnable {
callback.privacyChanged(privacyList)
}
+
private val updateListAndNotifyChanges = Runnable {
updatePrivacyList()
uiHandler.post(notifyChanges)
}
- private var listening = false
-
private val cb = object : AppOpsController.Callback {
override fun onActiveStateChanged(
code: Int,
@@ -61,12 +69,36 @@
) {
val userId = UserHandle.getUserId(uid)
if (userId in currentUserIds) {
- update()
+ update(false)
}
}
}
- private fun update() {
+ @VisibleForTesting
+ internal var userSwitcherReceiver = Receiver()
+ set(value) {
+ context.unregisterReceiver(field)
+ field = value
+ registerReceiver()
+ }
+
+ init {
+ registerReceiver()
+ }
+
+ private fun registerReceiver() {
+ context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
+ intents.forEach {
+ addAction(it)
+ }
+ }, null, null)
+ }
+
+ private fun update(updateUsers: Boolean) {
+ if (updateUsers) {
+ val currentUser = ActivityManager.getCurrentUser()
+ currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ }
bgHandler.post(updateListAndNotifyChanges)
}
@@ -75,7 +107,7 @@
listening = listen
if (listening) {
appOpsController.addCallback(OPS, cb)
- update()
+ update(true)
} else {
appOpsController.removeCallback(OPS, cb)
}
@@ -102,4 +134,12 @@
interface Callback {
fun privacyChanged(privacyItems: List<PrivacyItem>)
}
+
+ internal inner class Receiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action in intents) {
+ update(true)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 3da6d2e..bc38169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -112,8 +112,7 @@
return;
}
- alertEntry.mEntry.row.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ alertEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
if (alert) {
alertEntry.updateEntry(true /* updatePostTime */);
}
@@ -186,7 +185,7 @@
alertEntry.setEntry(entry);
mAlertEntries.put(entry.key, alertEntry);
onAlertEntryAdded(alertEntry);
- entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
/**
@@ -207,7 +206,7 @@
Entry entry = alertEntry.mEntry;
mAlertEntries.remove(key);
onAlertEntryRemoved(alertEntry);
- entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
alertEntry.reset();
if (mExtendedLifetimeAlertEntries.contains(entry)) {
if (mNotificationLifetimeFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index f1c0304..a5e7f04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -79,7 +79,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setAmbientPulsing(true);
+ entry.setAmbientPulsing(true);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, true);
}
@@ -88,11 +88,11 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setAmbientPulsing(false);
+ entry.setAmbientPulsing(false);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, false);
}
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fc1e94a..f045548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -199,7 +199,7 @@
for (int i = 0; i < N; i++) {
final NotificationData.Entry entry = activeNotifications.get(i);
- if (isMediaNotification(entry)) {
+ if (entry.isMediaNotification()) {
final MediaSession.Token token =
entry.notification.getNotification().extras.getParcelable(
Notification.EXTRA_MEDIA_SESSION);
@@ -336,13 +336,6 @@
return PlaybackState.STATE_NONE;
}
- private boolean isMediaNotification(NotificationData.Entry entry) {
- // TODO: confirm that there's a valid media key
- return entry.row.getExpandedContentView() != null
- && entry.row.getExpandedContentView().findViewById(
- com.android.internal.R.id.media_actions) != null;
- }
-
private void clearCurrentMediaNotificationSession() {
mMediaMetadata = null;
if (mMediaController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 8c53cc2..2ee5443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -393,7 +393,7 @@
}
public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -403,7 +403,7 @@
}
public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -532,7 +532,7 @@
// Ensure the entry hasn't already been removed. This can happen if there is an
// inflation exception while updating the remote history
- if (entry.row == null || entry.row.isRemoved()) {
+ if (entry.isRemoved()) {
return;
}
@@ -570,7 +570,7 @@
mEntryManager.updateNotification(newSbn, null);
- if (entry.row == null || entry.row.isRemoved()) {
+ if (entry.isRemoved()) {
return;
}
@@ -593,7 +593,7 @@
protected class RemoteInputActiveExtender extends RemoteInputExtender {
@Override
public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
- if (entry.row == null || entry.row.isDismissed()) {
+ if (entry.isDismissed()) {
return false;
}
return mRemoteInputController.isRemoteInputActive(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ea67736..daa2fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -51,6 +51,7 @@
public class NotificationViewHierarchyManager {
private static final String TAG = "NotificationViewHierarchyManager";
+ //TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -140,6 +141,7 @@
/**
* Updates the visual representation of the notifications.
*/
+ //TODO: Rewrite this to focus on Entries, or some other data object instead of views
public void updateNotificationViews() {
ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
.getActiveNotifications();
@@ -148,12 +150,12 @@
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationData.Entry ent = activeNotifications.get(i);
- if (ent.row.isDismissed() || ent.row.isRemoved()) {
+ if (ent.isRowDismissed() || ent.isRowRemoved()) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
}
- ent.row.setStatusBarState(mStatusBarStateListener.getCurrentState());
+ ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
&& mStatusBarStateListener.getCurrentState() == SHADE;
if (showAsBubble) {
@@ -175,20 +177,19 @@
boolean deviceSensitive = devicePublic
&& !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
mLockscreenUserManager.getCurrentUserId());
- ent.row.setSensitive(sensitive, deviceSensitive);
- ent.row.setNeedsRedaction(needsRedaction);
- if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
- ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
- ent.row.getStatusBarNotification());
+ ent.getRow().setSensitive(sensitive, deviceSensitive);
+ ent.getRow().setNeedsRedaction(needsRedaction);
+ if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
+ NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
List<ExpandableNotificationRow> orderedChildren =
- mTmpChildOrderMap.get(summary);
+ mTmpChildOrderMap.get(summary.getRow());
if (orderedChildren == null) {
orderedChildren = new ArrayList<>();
- mTmpChildOrderMap.put(summary, orderedChildren);
+ mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
}
- orderedChildren.add(ent.row);
+ orderedChildren.add(ent.getRow());
} else {
- toShow.add(ent.row);
+ toShow.add(ent.getRow());
}
}
@@ -391,19 +392,19 @@
&& !row.isLowPriority()));
}
- entry.row.setOnAmbient(getShadeController().isDozing());
+ entry.getRow().setOnAmbient(getShadeController().isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
- entry.notification) && !entry.row.isRemoved();
+ entry.notification) && !entry.isRowRemoved();
boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
.notification);
if (!showOnKeyguard) {
// min priority notifications should show if their summary is showing
if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
- ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+ NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
entry.notification);
if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
- summary.getStatusBarNotification())) {
+ summary.notification)) {
showOnKeyguard = true;
}
}
@@ -411,16 +412,16 @@
if (suppressedSummary
|| mLockscreenUserManager.shouldHideNotifications(userId)
|| (onKeyguard && !showOnKeyguard)) {
- entry.row.setVisibility(View.GONE);
+ entry.getRow().setVisibility(View.GONE);
} else {
- boolean wasGone = entry.row.getVisibility() == View.GONE;
+ boolean wasGone = entry.getRow().getVisibility() == View.GONE;
if (wasGone) {
- entry.row.setVisibility(View.VISIBLE);
+ entry.getRow().setVisibility(View.VISIBLE);
}
- if (!isChildNotification && !entry.row.isRemoved()) {
+ if (!isChildNotification && !entry.getRow().isRemoved()) {
if (wasGone) {
// notify the scroller of a child addition
- mListContainer.generateAddAnimation(entry.row,
+ mListContainer.generateAddAnimation(entry.getRow(),
!showOnKeyguard /* fromMoreCard */);
}
visibleNotifications++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 929f43e..e8abcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -250,16 +250,16 @@
// Make a copy because closing the remote inputs will modify mOpen.
ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
for (int i = mOpen.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = mOpen.get(i).first.get();
- if (item != null && item.row != null) {
- list.add(item);
+ NotificationData.Entry entry = mOpen.get(i).first.get();
+ if (entry != null && entry.rowExists()) {
+ list.add(entry);
}
}
for (int i = list.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = list.get(i);
- if (item.row != null) {
- item.row.closeRemoteInput();
+ NotificationData.Entry entry = list.get(i);
+ if (entry.rowExists()) {
+ entry.closeRemoteInput();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 758c33a..37bdc1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -41,12 +41,16 @@
mCallback = callback;
}
- public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
+ /**
+ * Notifies StatusBarService a smart reply is sent.
+ */
+ public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+ boolean generatedByAssistant) {
mCallback.onSmartReplySent(entry, reply);
mSendingKeys.add(entry.key);
try {
- mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
- replyIndex);
+ mBarService.onNotificationSmartReplySent(
+ entry.notification.getKey(), replyIndex, reply, generatedByAssistant);
} catch (RemoteException e) {
// Nothing to do, system going down
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index da6d977..d7680b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -64,6 +64,9 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -106,7 +109,6 @@
public int importance;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
- public ExpandableNotificationRow row; // the outer expanded view
private boolean interruption;
public boolean autoRedacted; // whether the redacted notification was generated by us
public int targetSdk;
@@ -119,6 +121,10 @@
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ private Entry parent; // our parent (if we're in a group)
+ private ArrayList<Entry> children = new ArrayList<Entry>();
+ private ExpandableNotificationRow row; // the outer expanded view
+
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
@@ -212,6 +218,24 @@
}
}
+ public ExpandableNotificationRow getRow() {
+ return row;
+ }
+
+ //TODO: This will go away when we have a way to bind an entry to a row
+ public void setRow(ExpandableNotificationRow row) {
+ this.row = row;
+ }
+
+ @Nullable
+ public List<Entry> getChildren() {
+ if (children.size() <= 0) {
+ return null;
+ }
+
+ return children;
+ }
+
public void notifyFullScreenIntentLaunched() {
setInterruption();
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -409,6 +433,198 @@
initializationTime = time;
}
}
+
+ public void sendAccessibilityEvent(int eventType) {
+ if (row != null) {
+ row.sendAccessibilityEvent(eventType);
+ }
+ }
+
+ /**
+ * Used by NotificationMediaManager to determine... things
+ * @return {@code true} if we are a media notification
+ */
+ public boolean isMediaNotification() {
+ if (row == null) return false;
+
+ return row.isMediaRow();
+ }
+
+ /**
+ * We are a top level child if our parent is the list of notifications duh
+ * @return {@code true} if we're a top level notification
+ */
+ public boolean isTopLevelChild() {
+ return row != null && row.isTopLevelChild();
+ }
+
+ public void resetUserExpansion() {
+ if (row != null) row.resetUserExpansion();
+ }
+
+ public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+ if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+ }
+
+ public void setAmbientPulsing(boolean pulsing) {
+ if (row != null) row.setAmbientPulsing(pulsing);
+ }
+
+ public boolean rowExists() {
+ return row != null;
+ }
+
+ public boolean isRowDismissed() {
+ return row != null && row.isDismissed();
+ }
+
+ public boolean isRowRemoved() {
+ return row != null && row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or removed
+ */
+ public boolean isRemoved() {
+ //TODO: recycling invalidates this
+ return row == null || row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or dismissed
+ */
+ public boolean isDismissed() {
+ //TODO: recycling
+ return row == null || row.isDismissed();
+ }
+
+ public boolean isRowPinned() {
+ return row != null && row.isPinned();
+ }
+
+ public void setRowPinned(boolean pinned) {
+ if (row != null) row.setPinned(pinned);
+ }
+
+ public boolean isRowAnimatingAway() {
+ return row != null && row.isHeadsUpAnimatingAway();
+ }
+
+ public boolean isRowHeadsUp() {
+ return row != null && row.isHeadsUp();
+ }
+
+ public void setHeadsUp(boolean shouldHeadsUp) {
+ if (row != null) row.setHeadsUp(shouldHeadsUp);
+ }
+
+ public boolean mustStayOnScreen() {
+ return row != null && row.mustStayOnScreen();
+ }
+
+ public void setHeadsUpIsVisible() {
+ if (row != null) row.setHeadsUpIsVisible();
+ }
+
+ //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+ public ExpandableNotificationRow getHeadsUpAnimationView() {
+ return row;
+ }
+
+ public void setUserLocked(boolean userLocked) {
+ if (row != null) row.setUserLocked(userLocked);
+ }
+
+ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+ if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+ }
+
+ public void setGroupExpansionChanging(boolean changing) {
+ if (row != null) row.setGroupExpansionChanging(changing);
+ }
+
+ public void notifyHeightChanged(boolean needsAnimation) {
+ if (row != null) row.notifyHeightChanged(needsAnimation);
+ }
+
+ public void closeRemoteInput() {
+ if (row != null) row.closeRemoteInput();
+ }
+
+ public boolean areChildrenExpanded() {
+ return row != null && row.areChildrenExpanded();
+ }
+
+ public boolean keepInParent() {
+ return row != null && row.keepInParent();
+ }
+
+ //TODO: probably less confusing to say "is group fully visible"
+ public boolean isGroupNotFullyVisible() {
+ return row == null || row.isGroupNotFullyVisible();
+ }
+
+ public NotificationGuts getGuts() {
+ if (row != null) return row.getGuts();
+ return null;
+ }
+
+ public boolean hasLowPriorityStateUpdated() {
+ return row != null && row.hasLowPriorityStateUpdated();
+ }
+
+ public void removeRow() {
+ if (row != null) row.setRemoved();
+ }
+
+ public boolean isSummaryWithChildren() {
+ return row != null && row.isSummaryWithChildren();
+ }
+
+ public void setKeepInParent(boolean keep) {
+ if (row != null) row.setKeepInParent(keep);
+ }
+
+ public void onDensityOrFontScaleChanged() {
+ if (row != null) row.onDensityOrFontScaleChanged();
+ }
+
+ public boolean areGutsExposed() {
+ return row != null && row.getGuts().isExposed();
+ }
+
+ public boolean isChildInGroup() {
+ return parent == null;
+ }
+
+ public void setLowPriorityStateUpdated(boolean updated) {
+ if (row != null) row.setLowPriorityStateUpdated(updated);
+ }
+
+ /**
+ * @return Can the underlying notification be cleared? This can be different from whether the
+ * notification can be dismissed in case notifications are sensitive on the lockscreen.
+ * @see #canViewBeDismissed()
+ */
+ public boolean isClearable() {
+ if (notification == null || !notification.isClearable()) {
+ return false;
+ }
+ if (children.size() > 0) {
+ for (int i = 0; i < children.size(); i++) {
+ Entry child = children.get(i);
+ if (!child.isClearable()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean canViewBeDismissed() {
+ if (row == null) return true;
+ return row.canViewBeDismissed();
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 450d34d..e333729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -385,9 +385,9 @@
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
- if (entry.row != null) {
+ if (entry.rowExists()) {
entry.reset();
- updateNotification(entry, pmUser, sbn, entry.row);
+ updateNotification(entry, pmUser, sbn, entry.getRow());
} else {
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
@@ -543,14 +543,14 @@
// Mark as seen immediately
setNotificationShown(entry.notification);
} else {
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
if (shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
}
}
@@ -561,20 +561,20 @@
mPendingNotifications.remove(entry.key);
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
- if (!entry.row.isRemoved()) {
+ if (!entry.isRowRemoved()) {
boolean isNew = mNotificationData.get(entry.key) == null;
if (isNew) {
showAlertingView(entry, inflatedFlags);
addEntry(entry);
} else {
- if (entry.row.hasLowPriorityStateUpdated()) {
+ if (entry.getRow().hasLowPriorityStateUpdated()) {
mVisualStabilityManager.onLowPriorityUpdated(entry);
mPresenter.updateNotificationViews();
}
mGroupAlertTransferHelper.onInflationFinished(entry);
}
}
- entry.row.setLowPriorityStateUpdated(false);
+ entry.setLowPriorityStateUpdated(false);
}
@Override
@@ -633,9 +633,9 @@
getMediaManager().onNotificationRemoved(key);
mForegroundServiceController.removeNotification(entry.notification);
- if (entry.row != null) {
- entry.row.setRemoved();
- mListContainer.cleanUpViewState(entry.row);
+ if (entry.rowExists()) {
+ entry.removeRow();
+ mListContainer.cleanUpViewStateForEntry(entry);
}
// Let's remove the children if this was a summary
@@ -670,19 +670,19 @@
*/
private void handleGroupSummaryRemoved(String key) {
NotificationData.Entry entry = mNotificationData.get(key);
- if (entry != null && entry.row != null
- && entry.row.isSummaryWithChildren()) {
- if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+ if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
+ if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
// We don't want to remove children for autobundled notifications as they are not
// always cancelled. We only remove them if they were dismissed by the user.
return;
}
- List<ExpandableNotificationRow> notificationChildren =
- entry.row.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow row = notificationChildren.get(i);
- NotificationData.Entry childEntry = row.getEntry();
- boolean isForeground = (row.getStatusBarNotification().getNotification().flags
+ List<NotificationData.Entry> childEntries = entry.getChildren();
+ if (childEntries == null) {
+ return;
+ }
+ for (int i = 0; i < childEntries.size(); i++) {
+ NotificationData.Entry childEntry = childEntries.get(i);
+ boolean isForeground = (entry.notification.getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
boolean keepForReply =
getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -692,10 +692,10 @@
// a child we're keeping around for reply!
continue;
}
- row.setKeepInParent(true);
+ entry.setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
- row.setRemoved();
+ entry.removeRow();
}
}
}
@@ -705,15 +705,15 @@
mNotificationData.getNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
NotificationData.Entry entry = userNotifications.get(i);
- boolean exposedGuts = mGutsManager.getExposedGuts() != null
- && entry.row.getGuts() == mGutsManager.getExposedGuts();
- entry.row.onDensityOrFontScaleChanged();
+ entry.onDensityOrFontScaleChanged();
+ boolean exposedGuts = entry.areGutsExposed();
if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry.row);
+ mGutsManager.onDensityOrFontScaleChanged(entry);
}
}
}
+ //TODO: This method associates a row with an entry, but eventually needs to not do that
protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row) {
boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
@@ -736,8 +736,8 @@
entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
- entry.row = row;
- entry.row.setOnActivatedListener(mPresenter);
+ entry.setRow(row);
+ row.setOnActivatedListener(mPresenter);
boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
mNotificationData.getImportance(sbn.getKey()));
@@ -910,7 +910,7 @@
if (!notification.isClearable()) {
// The user may have performed a dismiss action on the notification, since it's
// not clearable we should snap it back.
- mListContainer.snapViewIfNeeded(entry.row);
+ mListContainer.snapViewIfNeeded(entry);
}
if (DEBUG) {
@@ -963,11 +963,11 @@
if (NotificationUiAdjustment.needReinflate(
oldAdjustments.get(entry.key), newAdjustment)) {
- if (entry.row != null) {
+ if (entry.rowExists()) {
entry.reset();
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
- updateNotification(entry, pmUser, entry.notification, entry.row);
+ updateNotification(entry, pmUser, entry.notification, entry.getRow());
} else {
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 53ebe74..247c1ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -26,8 +26,8 @@
/**
* Returns whether an ExpandableNotificationRow is in a visible location or not.
*
- * @param row
+ * @param entry
* @return true if row is in a visible location
*/
- boolean isInVisibleLocation(ExpandableNotificationRow row);
+ boolean isInVisibleLocation(NotificationData.Entry entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 75613a4..fce7980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -120,7 +120,7 @@
return true;
}
if (mAllowedReorderViews.contains(row)
- && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
+ && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
return true;
}
return false;
@@ -142,12 +142,12 @@
if (isHeadsUp) {
// Heads up notifications should in general be allowed to reorder if they are out of
// view and stay at the current location if they aren't.
- mAllowedReorderViews.add(entry.row);
+ mAllowedReorderViews.add(entry.getRow());
}
}
public void onLowPriorityUpdated(NotificationData.Entry entry) {
- mLowPriorityReorderingViews.add(entry.row);
+ mLowPriorityReorderingViews.add(entry.getRow());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 5dfd5d0..87313b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -115,7 +115,7 @@
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
- boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+ boolean isVisible = mListContainer.isInVisibleLocation(entry);
NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
if (isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b6d99b2..23bfdad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -104,6 +104,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackScrollState;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -1411,16 +1412,16 @@
public void performDismiss(boolean fromAccessibility) {
if (isOnlyChildInGroup()) {
- ExpandableNotificationRow groupSummary =
+ NotificationData.Entry groupSummary =
mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
if (groupSummary.isClearable()) {
// If this is the only child in the group, dismiss the group, but don't try to show
// the blocking helper affordance!
- groupSummary.performDismiss(fromAccessibility);
+ groupSummary.getRow().performDismiss(fromAccessibility);
}
}
setDismissed(fromAccessibility);
- if (isClearable()) {
+ if (mEntry.isClearable()) {
// TODO: track dismiss sentiment
if (mOnDismissRunnable != null) {
mOnDismissRunnable.run();
@@ -2244,28 +2245,6 @@
setRippleAllowed(allowed);
}
- /**
- * @return Can the underlying notification be cleared? This can be different from whether the
- * notification can be dismissed in case notifications are sensitive on the lockscreen.
- * @see #canViewBeDismissed()
- */
- public boolean isClearable() {
- if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
- return false;
- }
- if (mIsSummaryWithChildren) {
- List<ExpandableNotificationRow> notificationChildren =
- mChildrenContainer.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow child = notificationChildren.get(i);
- if (!child.isClearable()) {
- return false;
- }
- }
- }
- return true;
- }
-
@Override
public int getIntrinsicHeight() {
if (isShownAsBubble()) {
@@ -2533,10 +2512,10 @@
/**
* @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
* otherwise some state might not be updated. To request about the general clearability
- * see {@link #isClearable()}.
+ * see {@link NotificationData.Entry#isClearable()}.
*/
public boolean canViewBeDismissed() {
- return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
private boolean shouldShowPublic() {
@@ -3038,6 +3017,21 @@
return mOnAmbient;
}
+ //TODO: this logic can't depend on layout if we are recycling!
+ public boolean isMediaRow() {
+ return getExpandedContentView() != null
+ && getExpandedContentView().findViewById(
+ com.android.internal.R.id.media_actions) != null;
+ }
+
+ public boolean isTopLevelChild() {
+ return getParent() instanceof NotificationStackScrollLayout;
+ }
+
+ public boolean isGroupNotFullyVisible() {
+ return getClipTopAmount() > 0 || getTranslationY() < 0;
+ }
+
public void setAboveShelf(boolean aboveShelf) {
boolean wasAboveShelf = isAboveShelf();
mAboveShelf = aboveShelf;
@@ -3145,8 +3139,10 @@
pw.print(", alpha: " + getAlpha());
pw.print(", translation: " + getTranslation());
pw.print(", removed: " + isRemoved());
- pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+ NotificationContentView showingLayout = getShowingLayout();
+ pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
pw.println();
+ showingLayout.dump(fd, pw, args);
pw.print(" ");
if (mNotificationViewState != null) {
mNotificationViewState.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index bb9a341..a4fdc08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -55,6 +55,9 @@
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyView;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
import java.util.List;
/**
@@ -1227,17 +1230,18 @@
mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
+ ExpandableNotificationRow row = entry.getRow();
if (mContractedChild != null) {
- mContractedWrapper.onContentUpdated(entry.row);
+ mContractedWrapper.onContentUpdated(row);
}
if (mExpandedChild != null) {
- mExpandedWrapper.onContentUpdated(entry.row);
+ mExpandedWrapper.onContentUpdated(row);
}
if (mHeadsUpChild != null) {
- mHeadsUpWrapper.onContentUpdated(entry.row);
+ mHeadsUpWrapper.onContentUpdated(row);
}
if (mAmbientChild != null) {
- mAmbientWrapper.onContentUpdated(entry.row);
+ mAmbientWrapper.onContentUpdated(row);
}
applyRemoteInputAndSmartReply(entry);
updateLegacy();
@@ -1287,10 +1291,10 @@
return;
}
- SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
- mSmartReplyConstants, entry);
+ SmartRepliesAndActions smartRepliesAndActions =
+ chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
- applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+ applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
applySmartReplyView(smartRepliesAndActions, entry);
}
@@ -1317,58 +1321,47 @@
boolean appGeneratedSmartRepliesExist =
enableAppGeneratedSmartReplies
&& remoteInputActionPair != null
- && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+ && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
+ && remoteInputActionPair.second.actionIntent != null;
List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+ SmartReplyView.SmartReplies smartReplies = null;
+ SmartReplyView.SmartActions smartActions = null;
if (appGeneratedSmartRepliesExist) {
- return new SmartRepliesAndActions(remoteInputActionPair.first,
- remoteInputActionPair.second.actionIntent,
+ smartReplies = new SmartReplyView.SmartReplies(
remoteInputActionPair.first.getChoices(),
- appGeneratedSmartActions,
- freeformRemoteInputActionPair);
- } else if (appGeneratedSmartActionsExist) {
- return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
- freeformRemoteInputActionPair);
- } else if (!ArrayUtils.isEmpty(entry.smartReplies)
- && freeformRemoteInputActionPair != null
- && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
- // App didn't generate anything, use NAS-generated replies and actions
- return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
- freeformRemoteInputActionPair.second.actionIntent,
- entry.smartReplies,
- entry.systemGeneratedSmartActions,
- freeformRemoteInputActionPair);
+ remoteInputActionPair.first,
+ remoteInputActionPair.second.actionIntent,
+ false /* fromAssistant */);
}
- // App didn't generate anything, and there are no NAS-generated smart replies.
- return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
- freeformRemoteInputActionPair);
- }
-
- @VisibleForTesting
- static class SmartRepliesAndActions {
- public final RemoteInput remoteInputWithChoices;
- public final PendingIntent pendingIntentForSmartReplies;
- public final CharSequence[] smartReplies;
- public final List<Notification.Action> smartActions;
- public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
-
- SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
- CharSequence[] choices, List<Notification.Action> smartActions,
- Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
- this.remoteInputWithChoices = remoteInput;
- this.pendingIntentForSmartReplies = pendingIntent;
- this.smartReplies = choices;
- this.smartActions = smartActions;
- this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
+ if (appGeneratedSmartActionsExist) {
+ smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
+ false /* fromAssistant */);
}
-
- boolean smartRepliesExist() {
- return remoteInputWithChoices != null
- && pendingIntentForSmartReplies != null
- && !ArrayUtils.isEmpty(smartReplies);
+ // Apps didn't provide any smart replies / actions, use those from NAS (if any).
+ if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
+ boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+ && freeformRemoteInputActionPair != null
+ && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
+ && freeformRemoteInputActionPair.second.actionIntent != null;
+ if (useGeneratedReplies) {
+ smartReplies = new SmartReplyView.SmartReplies(
+ entry.smartReplies,
+ freeformRemoteInputActionPair.first,
+ freeformRemoteInputActionPair.second.actionIntent,
+ true /* fromAssistant */);
+ }
+ boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
+ && notification.getAllowSystemGeneratedContextualActions();
+ if (useSmartActions) {
+ smartActions = new SmartReplyView.SmartActions(
+ entry.systemGeneratedSmartActions, true /* fromAssistant */);
+ }
}
+ return new SmartRepliesAndActions(
+ smartReplies, smartActions, freeformRemoteInputActionPair != null);
}
private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1475,12 +1468,9 @@
if (mExpandedChild != null) {
mExpandedSmartReplyView =
applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
- if (mExpandedSmartReplyView != null
- && smartRepliesAndActions.remoteInputWithChoices != null
- && smartRepliesAndActions.smartReplies != null
- && smartRepliesAndActions.smartReplies.length > 0) {
- mSmartReplyController.smartRepliesAdded(entry,
- smartRepliesAndActions.smartReplies.length);
+ if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
+ mSmartReplyController.smartRepliesAdded(
+ entry, smartRepliesAndActions.smartReplies.choices.length);
}
}
}
@@ -1494,8 +1484,8 @@
}
LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
// If there are no smart replies and no smart actions - early out.
- if (!smartRepliesAndActions.smartRepliesExist()
- && smartRepliesAndActions.smartActions.isEmpty()) {
+ if (smartRepliesAndActions.smartReplies == null
+ && smartRepliesAndActions.smartActions == null) {
smartReplyContainer.setVisibility(View.GONE);
return null;
}
@@ -1525,10 +1515,13 @@
}
if (smartReplyView != null) {
smartReplyView.resetSmartSuggestions(smartReplyContainer);
- smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
- smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
- entry, smartRepliesAndActions.smartReplies);
- smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+ if (smartRepliesAndActions.smartReplies != null) {
+ smartReplyView.addRepliesFromRemoteInput(
+ smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
+ }
+ if (smartRepliesAndActions.smartActions != null) {
+ smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+ }
smartReplyContainer.setVisibility(View.VISIBLE);
}
return smartReplyView;
@@ -1928,4 +1921,41 @@
mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
}
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" ");
+ pw.print("contentView visibility: " + getVisibility());
+ pw.print(", alpha: " + getAlpha());
+ pw.print(", clipBounds: " + getClipBounds());
+ pw.print(", contentHeight: " + mContentHeight);
+ pw.print(", visibleType: " + mVisibleType);
+ View view = getViewForVisibleType(mVisibleType);
+ pw.print(", visibleView ");
+ if (view != null) {
+ pw.print(" visibility: " + view.getVisibility());
+ pw.print(", alpha: " + view.getAlpha());
+ pw.print(", clipBounds: " + view.getClipBounds());
+ } else {
+ pw.print("null");
+ }
+ pw.println();
+ }
+
+ @VisibleForTesting
+ static class SmartRepliesAndActions {
+ @Nullable
+ public final SmartReplyView.SmartReplies smartReplies;
+ @Nullable
+ public final SmartReplyView.SmartActions smartActions;
+ public final boolean hasFreeformRemoteInput;
+
+ SmartRepliesAndActions(
+ @Nullable SmartReplyView.SmartReplies smartReplies,
+ @Nullable SmartReplyView.SmartActions smartActions,
+ boolean hasFreeformRemoteInput) {
+ this.smartReplies = smartReplies;
+ this.smartActions = smartActions;
+ this.hasFreeformRemoteInput = hasFreeformRemoteInput;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 37bf06e..7895a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -86,7 +86,6 @@
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
// which notification is currently being longpress-examined by the user
- private final IStatusBarService mBarService;
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
@@ -103,8 +102,6 @@
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -116,9 +113,9 @@
mOnSettingsClickListener = onSettingsClick;
}
- public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
- setExposedGuts(row.getGuts());
- bindGuts(row);
+ public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+ setExposedGuts(entry.getGuts());
+ bindGuts(entry.getRow());
}
/**
@@ -441,8 +438,8 @@
public boolean shouldExtendLifetime(NotificationData.Entry entry) {
return entry != null
&&(mNotificationGutsExposed != null
- && entry.row.getGuts() != null
- && mNotificationGutsExposed == entry.row.getGuts()
+ && entry.getGuts() != null
+ && mNotificationGutsExposed == entry.getGuts()
&& !mNotificationGutsExposed.isLeavebehind());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 3a7091b..0d36d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -92,6 +92,7 @@
private String mPackageName;
private String mAppName;
private int mAppUid;
+ private String mDelegatePkg;
private int mNumUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
@@ -235,6 +236,7 @@
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
mIsForBlockingHelper = isForBlockingHelper;
mAppUid = mSbn.getUid();
+ mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mIsNoisy = isNoisy;
@@ -281,26 +283,8 @@
((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
- // Set group information if this channel has an associated group.
- CharSequence groupName = null;
- if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
- final NotificationChannelGroup notificationChannelGroup =
- mINotificationManager.getNotificationChannelGroupForPackage(
- mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
- if (notificationChannelGroup != null) {
- groupName = notificationChannelGroup.getName();
- }
- }
- TextView groupNameView = findViewById(R.id.group_name);
- TextView groupDividerView = findViewById(R.id.pkg_group_divider);
- if (groupName != null) {
- groupNameView.setText(groupName);
- groupNameView.setVisibility(View.VISIBLE);
- groupDividerView.setVisibility(View.VISIBLE);
- } else {
- groupNameView.setVisibility(View.GONE);
- groupDividerView.setVisibility(View.GONE);
- }
+ // Delegate
+ bindDelegate();
// Settings button.
final View settingsButton = findViewById(R.id.info);
@@ -320,9 +304,10 @@
}
}
- private void bindPrompt() {
+ private void bindPrompt() throws RemoteException {
final TextView blockPrompt = findViewById(R.id.block_prompt);
bindName();
+ bindGroup();
if (mIsNonblockable) {
blockPrompt.setText(R.string.notification_unblockable_desc);
} else {
@@ -345,6 +330,60 @@
}
}
+ private void bindDelegate() {
+ TextView delegateView = findViewById(R.id.delegate_name);
+ TextView dividerView = findViewById(R.id.pkg_divider);
+
+ CharSequence delegatePkg = null;
+ if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+ // this notification was posted by a delegate!
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ mDelegatePkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) { }
+ }
+ if (delegatePkg != null) {
+ delegateView.setText(mContext.getResources().getString(
+ R.string.notification_delegate_header, delegatePkg));
+ delegateView.setVisibility(View.VISIBLE);
+ dividerView.setVisibility(View.VISIBLE);
+ } else {
+ delegateView.setVisibility(View.GONE);
+ dividerView.setVisibility(View.GONE);
+ }
+ }
+
+ private void bindGroup() throws RemoteException {
+ // Set group information if this channel has an associated group.
+ CharSequence groupName = null;
+ if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+ final NotificationChannelGroup notificationChannelGroup =
+ mINotificationManager.getNotificationChannelGroupForPackage(
+ mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+ if (notificationChannelGroup != null) {
+ groupName = notificationChannelGroup.getName();
+ }
+ }
+ TextView groupNameView = findViewById(R.id.group_name);
+ TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+ if (groupName != null) {
+ groupNameView.setText(groupName);
+ groupNameView.setVisibility(View.VISIBLE);
+ groupDividerView.setVisibility(View.VISIBLE);
+ } else {
+ groupNameView.setVisibility(View.GONE);
+ groupDividerView.setVisibility(View.GONE);
+ }
+ }
+
@VisibleForTesting
void logBlockingHelperCounter(String counterTag) {
if (mIsForBlockingHelper) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 4d100a4..6de4fc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -118,9 +118,9 @@
/**
* Handle snapping a non-dismissable row back if the user tried to dismiss it.
*
- * @param row row to snap back
+ * @param entry the entry whose row needs to snap back
*/
- void snapViewIfNeeded(ExpandableNotificationRow row);
+ void snapViewIfNeeded(NotificationData.Entry entry);
/**
* Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
@@ -149,9 +149,9 @@
* Called when a notification is removed from the shade. This cleans up the state for a
* given view.
*
- * @param view view to clean up view state for
+ * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
*/
- void cleanUpViewState(View view);
+ void cleanUpViewStateForEntry(NotificationData.Entry entry);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index c867a41..27838a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -21,6 +21,7 @@
import android.view.View;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -50,13 +51,13 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- updateView(headsUp, false /* animate */);
+ public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+ updateView(headsUp.getRow(), false /* animate */);
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
- updateView(headsUp, true /* animate */);
+ public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+ updateView(headsUp.getRow(), true /* animate */);
}
public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ecd0d98..faccff3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -600,12 +600,12 @@
public void setRemoteInputActive(NotificationData.Entry entry,
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
- entry.row.notifyHeightChanged(true /* needsAnimation */);
+ entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
}
public void lockScrollTo(NotificationData.Entry entry) {
- NotificationStackScrollLayout.this.lockScrollTo(entry.row);
+ NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
}
public void requestDisallowLongPressAndDismiss() {
@@ -897,7 +897,8 @@
@Override
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+ public boolean isInVisibleLocation(NotificationData.Entry entry) {
+ ExpandableNotificationRow row = entry.getRow();
ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
if (childViewState == null) {
return false;
@@ -1213,12 +1214,12 @@
if (topEntry == null) {
return 0;
}
- ExpandableNotificationRow row = topEntry.row;
+ ExpandableNotificationRow row = topEntry.getRow();
if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
+ final NotificationData.Entry groupSummary
= mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null) {
- row = groupSummary;
+ row = groupSummary.getRow();
}
}
return row.getPinnedHeadsUpHeight();
@@ -1390,11 +1391,12 @@
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+ NotificationData.Entry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().row != row
+ && mHeadsUpManager.getTopEntry().getRow() != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
- != row) {
+ mHeadsUpManager.getTopEntry().notification)
+ != entry) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
@@ -1524,7 +1526,8 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void snapViewIfNeeded(ExpandableNotificationRow child) {
+ public void snapViewIfNeeded(NotificationData.Entry entry) {
+ ExpandableNotificationRow child = entry.getRow();
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
@@ -2514,7 +2517,8 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@Override
- public void cleanUpViewState(View child) {
+ public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+ View child = entry.getRow();
if (child == mSwipeHelper.getTranslatingParentView()) {
mSwipeHelper.clearTranslatingParentView();
}
@@ -2644,9 +2648,9 @@
private boolean isChildInInvisibleGroup(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- ExpandableNotificationRow groupSummary =
+ NotificationData.Entry groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null && groupSummary != row) {
+ if (groupSummary != null && groupSummary.getRow() != row) {
return row.getVisibility() == View.INVISIBLE;
}
}
@@ -4662,6 +4666,11 @@
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
}
+ public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+ ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+ generateHeadsUpAnimation(row, isHeadsUp);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
@@ -5692,7 +5701,7 @@
&& (parent.areGutsExposed()
|| mSwipeHelper.getExposedMenuView() == parent
|| (parent.getNotificationChildren().size() == 1
- && parent.isClearable()))) {
+ && parent.getEntry().isClearable()))) {
// In this case the group is expanded and showing the menu for the
// group, further interaction should apply to the group, not any
// child notifications so we use the parent of the child. We also do the same
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 40f9f45..3c8cad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -143,9 +143,9 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
updateTopEntry();
- updateHeader(headsUp.getEntry());
+ updateHeader(entry);
}
/** To count the distance from the window right boundary to scroller right boundary. The
@@ -298,9 +298,9 @@
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
updateTopEntry();
- updateHeader(headsUp.getEntry());
+ updateHeader(entry);
}
public void setExpandedHeight(float expandedHeight, float appearFraction) {
@@ -339,7 +339,7 @@
}
public void updateHeader(NotificationData.Entry entry) {
- ExpandableNotificationRow row = entry.row;
+ ExpandableNotificationRow row = entry.getRow();
float headerVisibleAmount = 1.0f;
if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
headerVisibleAmount = mExpandFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 00d6b14..9faada0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -304,18 +304,19 @@
return;
}
if (hasPinnedHeadsUp()) {
- ExpandableNotificationRow topEntry = getTopEntry().row;
+ NotificationData.Entry topEntry = getTopEntry();
if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ final NotificationData.Entry groupSummary
+ = mGroupManager.getGroupSummary(topEntry.notification);
if (groupSummary != null) {
topEntry = groupSummary;
}
}
- topEntry.getLocationOnScreen(mTmpTwoArray);
+ ExpandableNotificationRow topRow = topEntry.getRow();
+ topRow.getLocationOnScreen(mTmpTwoArray);
int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int height = topEntry.getIntrinsicHeight();
+ int maxX = mTmpTwoArray[0] + topRow.getWidth();
+ int height = topRow.getIntrinsicHeight();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index be4df45..9c1c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -84,8 +84,8 @@
// We might touch above the visible heads up child, but then we still would
// like to capture it.
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
- if (topEntry != null && topEntry.row.isPinned()) {
- mPickedChild = topEntry.row;
+ if (topEntry != null && topEntry.isRowPinned()) {
+ mPickedChild = topEntry.getRow();
mTouchingHeadsUpView = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 0cf1b3d..8657003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -56,7 +56,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -863,16 +862,9 @@
public static View create(Context context, FragmentListener listener) {
final int displayId = context.getDisplay().getDisplayId();
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- final int height = isDefaultDisplay
- ? LayoutParams.MATCH_PARENT
- : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, height,
- // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
- isDefaultDisplay
- ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
- : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -884,10 +876,6 @@
lp.setTitle("NavigationBar" + displayId);
lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
- if (!isDefaultDisplay) {
- lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
- lp.gravity = Gravity.BOTTOM;
- }
View navigationBarView = LayoutInflater.from(context).inflate(
R.layout.navigation_bar_window, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index c74514e..3e31fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -198,7 +198,7 @@
alertNotificationWhenPossible(entry, getActiveAlertManager());
} else {
// The transfer is no longer valid. Free the content.
- entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+ entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
}
}
}
@@ -299,9 +299,9 @@
Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
if (child != null) {
- if (child.row.keepInParent()
- || child.row.isRemoved()
- || child.row.isDismissed()) {
+ if (child.getRow().keepInParent()
+ || child.isRowRemoved()
+ || child.isRowDismissed()) {
// The notification is actually already removed. No need to alert it.
return;
}
@@ -390,10 +390,10 @@
private void alertNotificationWhenPossible(@NonNull Entry entry,
@NonNull AlertingNotificationManager alertManager) {
@InflationFlag int contentFlag = alertManager.getContentFlag();
- if (!entry.row.isInflationFlagSet(contentFlag)) {
+ if (!entry.getRow().isInflationFlagSet(contentFlag)) {
mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
- entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
- entry.row.inflateViews();
+ entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+ entry.getRow().inflateViews();
return;
}
if (alertManager.isAlerting(entry.key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8ceabf8..448b5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -87,8 +87,7 @@
group.expanded = expanded;
if (group.summary != null) {
for (OnGroupChangeListener listener : mListeners) {
- listener.onGroupExpansionChanged(group.summary.row,
- expanded);
+ listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
}
}
}
@@ -133,7 +132,7 @@
}
public void onEntryAdded(final NotificationData.Entry added) {
- if (added.row.isRemoved()) {
+ if (added.isRowRemoved()) {
added.setDebugThrowable(new Throwable());
}
final StatusBarNotification sbn = added.notification;
@@ -152,17 +151,17 @@
if (existing != null && existing != added) {
Throwable existingThrowable = existing.getDebugThrowable();
Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
- + "existing removed: " + existing.row.isRemoved()
+ + "existing removed: " + existing.isRowRemoved()
+ (existingThrowable != null
? Log.getStackTraceString(existingThrowable) + "\n": "")
- + " added removed" + added.row.isRemoved()
+ + " added removed" + added.isRowRemoved()
, new Throwable());
}
group.children.put(added.key, added);
updateSuppression(group);
} else {
group.summary = added;
- group.expanded = added.row.areChildrenExpanded();
+ group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
ArrayList<NotificationData.Entry> childrenCopy
@@ -263,9 +262,9 @@
if (!isOnlyChild(sbn)) {
return false;
}
- ExpandableNotificationRow logicalGroupSummary = getLogicalGroupSummary(sbn);
+ NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
return logicalGroupSummary != null
- && !logicalGroupSummary.getStatusBarNotification().equals(sbn);
+ && !logicalGroupSummary.notification.equals(sbn);
}
private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,7 +338,7 @@
* Get the summary of a specified status bar notification. For isolated notification this return
* itself.
*/
- public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
+ public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(getGroupKey(sbn));
}
@@ -348,16 +347,17 @@
* but the logical summary, i.e when a child is isolated, it still returns the summary as if
* it wasn't isolated.
*/
- public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
+ public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(sbn.getGroupKey());
}
@Nullable
- private ExpandableNotificationRow getGroupSummary(String groupKey) {
+ private NotificationData.Entry getGroupSummary(String groupKey) {
NotificationGroup group = mGroupMap.get(groupKey);
+ //TODO: see if this can become an Entry
return group == null ? null
: group.summary == null ? null
- : group.summary.row;
+ : group.summary;
}
/**
@@ -438,11 +438,11 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
}
@Override
@@ -533,8 +533,7 @@
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
return notificationGroup.summary == null
- || notificationGroup.summary.row.getClipTopAmount() > 0
- || notificationGroup.summary.row.getTranslationY() < 0;
+ || notificationGroup.summary.isGroupNotFullyVisible();
}
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 184766c..2d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -192,13 +192,13 @@
&& !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
return false;
}
- if (!StatusBar.isTopLevelChild(entry)) {
+ if (!entry.isTopLevelChild()) {
return false;
}
- if (entry.row.getVisibility() == View.GONE) {
+ if (entry.getRow().getVisibility() == View.GONE) {
return false;
}
- if (entry.row.isDismissed() && hideDismissed) {
+ if (entry.isRowDismissed() && hideDismissed) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 851e6d0..33d176a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2500,25 +2500,26 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && headsUp.isHeadsUp()) {
- mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
- headsUp.setHeadsUpIsVisible();
+ if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+ mNotificationStackScroller.generateHeadsUpAnimation(
+ entry.getHeadsUpAnimationView(), false);
+ entry.setHeadsUpIsVisible();
}
}
@Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
- mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+ mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index bdddf5b..05f8f18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -484,7 +484,7 @@
private Runnable mLaunchTransitionEndRunnable;
protected boolean mLaunchTransitionFadingAway;
- private ExpandableNotificationRow mDraggedDownRow;
+ private NotificationData.Entry mDraggedDownEntry;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private int mLastCameraLaunchSource;
@@ -1265,10 +1265,6 @@
mQSPanel.clickTile(tile);
}
- public static boolean isTopLevelChild(Entry entry) {
- return entry.row.getParent() instanceof NotificationStackScrollLayout;
- }
-
public boolean areNotificationsHidden() {
return mZenController.areNotificationsHiddenInShade();
}
@@ -1491,12 +1487,12 @@
}
@Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpPinned(NotificationData.Entry entry) {
dismissVolumeDialog();
}
@Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ public void onHeadsUpUnPinned(NotificationData.Entry entry) {
}
@Override
@@ -2605,9 +2601,7 @@
final int notificationCount = activeNotifications.size();
for (int i = 0; i < notificationCount; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
- if (entry.row != null) {
- entry.row.resetUserExpansion();
- }
+ entry.resetUserExpansion();
}
}
@@ -3038,10 +3032,10 @@
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
- if (mDraggedDownRow != null) {
- mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
- mDraggedDownRow = null;
+ if (mDraggedDownEntry != null) {
+ mDraggedDownEntry.setUserLocked(false);
+ mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
+ mDraggedDownEntry = null;
}
}
@@ -3181,9 +3175,9 @@
}
long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
mNotificationPanel.animateToFullShade(delay);
- if (mDraggedDownRow != null) {
- mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow = null;
+ if (mDraggedDownEntry != null) {
+ mDraggedDownEntry.setUserLocked(false);
+ mDraggedDownEntry = null;
}
// TODO(115978725): Support animations on external nav bars.
@@ -3575,14 +3569,15 @@
int userId = mLockscreenUserManager.getCurrentUserId();
ExpandableNotificationRow row = null;
+ NotificationData.Entry entry = null;
if (expandView instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) expandView;
- row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+ entry = ((ExpandableNotificationRow) expandView).getEntry();
+ entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
- row.setGroupExpansionChanging(true);
- if (row.getStatusBarNotification() != null) {
- userId = row.getStatusBarNotification().getUserId();
+ entry.setGroupExpansionChanging(true);
+ if (entry.notification != null) {
+ userId = entry.notification.getUserId();
}
}
boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
@@ -3592,7 +3587,7 @@
if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
showBouncerIfKeyguard();
- mDraggedDownRow = row;
+ mDraggedDownEntry = entry;
mPendingRemoteInputView = null;
} else {
mNotificationPanel.animateToFullShade(0 /* delay */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index edfc049..588c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -418,7 +418,7 @@
StatusBarNotification parentToCancel = null;
if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
StatusBarNotification summarySbn =
- mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+ mGroupManager.getLogicalGroupSummary(sbn).notification;
if (shouldAutoCancel(summarySbn)) {
parentToCancel = summarySbn;
}
@@ -591,7 +591,7 @@
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
- mShadeController.goToLockedShade(clickedEntry.row);
+ mShadeController.goToLockedShade(clickedEntry.getRow());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fdab616..e7280643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -123,15 +123,15 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setEntryPinned: " + isPinned);
}
- ExpandableNotificationRow row = headsUpEntry.mEntry.row;
- if (row.isPinned() != isPinned) {
- row.setPinned(isPinned);
+ NotificationData.Entry entry = headsUpEntry.mEntry;
+ if (entry.isRowPinned() != isPinned) {
+ entry.setRowPinned(isPinned);
updatePinnedMode();
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
- listener.onHeadsUpPinned(row);
+ listener.onHeadsUpPinned(entry);
} else {
- listener.onHeadsUpUnPinned(row);
+ listener.onHeadsUpUnPinned(entry);
}
}
}
@@ -144,7 +144,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setHeadsUp(true);
+ entry.setHeadsUp(true);
setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, true);
@@ -154,12 +154,12 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
- entry.row.setHeadsUp(false);
+ entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
- entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
protected void updatePinnedMode() {
@@ -282,7 +282,7 @@
private boolean hasPinnedNotificationInternal() {
for (String key : mAlertEntries.keySet()) {
AlertEntry entry = getHeadsUpEntry(key);
- if (entry.mEntry.row.isPinned()) {
+ if (entry.mEntry.isRowPinned()) {
return true;
}
}
@@ -302,10 +302,9 @@
// when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
// on the screen.
- if (userUnPinned && entry.mEntry != null && entry.mEntry.row != null) {
- ExpandableNotificationRow row = entry.mEntry.row;
- if (row.mustStayOnScreen()) {
- row.setHeadsUpIsVisible();
+ if (userUnPinned && entry.mEntry != null) {
+ if (entry.mEntry.mustStayOnScreen()) {
+ entry.mEntry.setHeadsUpIsVisible();
}
}
}
@@ -341,7 +340,7 @@
*/
public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
- if (headsUpEntry != null && entry.row.isPinned()) {
+ if (headsUpEntry != null && entry.isRowPinned()) {
headsUpEntry.setExpanded(expanded);
}
}
@@ -365,15 +364,15 @@
@Override
protected boolean isSticky() {
- return (mEntry.row.isPinned() && expanded)
+ return (mEntry.isRowPinned() && expanded)
|| remoteInputActive || hasFullScreenIntent(mEntry);
}
@Override
public int compareTo(@NonNull AlertEntry alertEntry) {
HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
- boolean isPinned = mEntry.row.isPinned();
- boolean otherPinned = headsUpEntry.mEntry.row.isPinned();
+ boolean isPinned = mEntry.isRowPinned();
+ boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
if (isPinned && !otherPinned) {
return -1;
} else if (!isPinned && otherPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index d434768..7ad547a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -33,12 +33,12 @@
/**
* A notification was just pinned to the top.
*/
- default void onHeadsUpPinned(ExpandableNotificationRow headsUp) {}
+ default void onHeadsUpPinned(NotificationData.Entry entry) {}
/**
* A notification was just unpinned from the top.
*/
- default void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {}
+ default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
/**
* A notification just became a heads up or turned back to its normal state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a485fa8..866015e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -245,7 +245,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (mEntry.row.isChangingPosition()) {
+ if (mEntry.getRow().isChangingPosition()) {
if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
mEditText.requestFocus();
}
@@ -255,7 +255,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
+ if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
mController.removeRemoteInput(mEntry, mToken);
@@ -495,7 +495,7 @@
}
private void defocusIfNeeded(boolean animate) {
- if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
+ if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
|| isTemporarilyDetached()) {
if (isTemporarilyDetached()) {
// We might get reattached but then the other one of HUN / expanded might steal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 0186683..88ff078 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.policy;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
@@ -189,15 +190,13 @@
* into the notification are shown.
*/
public void addRepliesFromRemoteInput(
- RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry,
- CharSequence[] choices) {
- if (remoteInput != null && pendingIntent != null) {
- if (choices != null) {
- for (int i = 0; i < choices.length; ++i) {
+ SmartReplies smartReplies,
+ SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
+ if (smartReplies.choices != null) {
+ for (int i = 0; i < smartReplies.choices.length; ++i) {
Button replyButton = inflateReplyButton(
- getContext(), this, i, choices[i], remoteInput, pendingIntent,
- smartReplyController, entry);
+ getContext(), this, i, smartReplies, smartReplyController, entry);
addView(replyButton);
}
}
@@ -209,10 +208,10 @@
* Add smart actions to be shown next to smart replies. Only the actions that fit into the
* notification are shown.
*/
- public void addSmartActions(List<Notification.Action> smartActions) {
- int numSmartActions = smartActions.size();
+ public void addSmartActions(SmartActions smartActions) {
+ int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
- Notification.Action action = smartActions.get(n);
+ Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
Button actionButton = inflateActionButton(getContext(), this, action);
addView(actionButton);
@@ -228,22 +227,25 @@
@VisibleForTesting
Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
- CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ SmartReplies smartReplies, SmartReplyController smartReplyController,
+ NotificationData.Entry entry) {
Button b = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_reply_button, root, false);
+ CharSequence choice = smartReplies.choices[replyIndex];
b.setText(choice);
OnDismissAction action = () -> {
- smartReplyController.smartReplySent(entry, replyIndex, b.getText());
+ smartReplyController.smartReplySent(
+ entry, replyIndex, b.getText(), smartReplies.fromAssistant);
Bundle results = new Bundle();
- results.putString(remoteInput.getResultKey(), choice.toString());
+ results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+ RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+ results);
RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
entry.setHasSentReply();
try {
- pendingIntent.send(context, 0, intent);
+ smartReplies.pendingIntent.send(context, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Unable to send smart reply", e);
}
@@ -741,4 +743,40 @@
return show;
}
}
+
+ /**
+ * Data class for smart replies.
+ */
+ public static class SmartReplies {
+ @NonNull
+ public final RemoteInput remoteInput;
+ @NonNull
+ public final PendingIntent pendingIntent;
+ @NonNull
+ public final CharSequence[] choices;
+ public final boolean fromAssistant;
+
+ public SmartReplies(CharSequence[] choices, RemoteInput remoteInput,
+ PendingIntent pendingIntent, boolean fromAssistant) {
+ this.choices = choices;
+ this.remoteInput = remoteInput;
+ this.pendingIntent = pendingIntent;
+ this.fromAssistant = fromAssistant;
+ }
+ }
+
+
+ /**
+ * Data class for smart actions.
+ */
+ public static class SmartActions {
+ @NonNull
+ public final List<Notification.Action> actions;
+ public final boolean fromAssistant;
+
+ public SmartActions(List<Notification.Action> actions, boolean fromAssistant) {
+ this.actions = actions;
+ this.fromAssistant = fromAssistant;
+ }
+ }
}
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 a26b1b5..0953951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,11 +19,9 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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;
@@ -34,10 +32,15 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.core.SliceQuery;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
import org.junit.Assert;
import org.junit.Before;
@@ -50,13 +53,6 @@
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
-import androidx.slice.Slice;
-import androidx.slice.SliceItem;
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.core.SliceQuery;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -164,7 +160,7 @@
}
@Override
- protected boolean isDndSuppressingNotifications() {
+ protected boolean isDndOn() {
return mIsZenMode;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 221cbe9..c28e74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -37,6 +37,7 @@
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
@@ -127,6 +128,8 @@
resources.addOverride(R.integer.config_warningTemperature, 55);
mPowerUI.start();
+ // Guarantees mHandler has processed all messages.
+ TestableLooper.get(this).processAllMessages();
verify(mMockWarnings).showHighTemperatureWarning();
}
@@ -139,6 +142,8 @@
resources.addOverride(R.integer.config_warningTemperature, 55);
mPowerUI.start();
+ // Guarantees mHandler has processed all messages.
+ TestableLooper.get(this).processAllMessages();
verify(mMockWarnings).showHighTemperatureWarning();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 48491d7..24bcca50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -16,8 +16,12 @@
package com.android.systemui.privacy
+import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.Intent
import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
import android.support.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -34,9 +38,11 @@
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
-
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -44,10 +50,18 @@
@RunWithLooper
class PrivacyItemControllerTest : SysuiTestCase() {
+ companion object {
+ val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+ val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+ const val TAG = "PrivacyItemControllerTest"
+ }
+
@Mock
private lateinit var appOpsController: AppOpsController
@Mock
private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
@@ -57,15 +71,17 @@
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java)
+ appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+ mContext.addMockSystemService(UserManager::class.java, userManager)
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
privacyItemController = PrivacyItemController(mContext, callback)
}
+
@Test
fun testSetListeningTrue() {
privacyItemController.setListening(true)
@@ -80,6 +96,38 @@
privacyItemController.setListening(true)
privacyItemController.setListening(false)
verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
- any(AppOpsController.Callback:: class.java))
+ any(AppOpsController.Callback::class.java))
+ }
+
+ @Test
+ fun testRegisterReceiver_allUsers() {
+ val spiedContext = spy(mContext)
+ val itemController = PrivacyItemController(spiedContext, callback)
+
+ verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
+ eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
+ eq(null))
+ verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
+ }
+
+ @Test
+ fun testReceiver_ACTION_USER_FOREGROUND() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_USER_FOREGROUND))
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
+ verify(userManager).getProfiles(anyInt())
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f49c5b4..9d0556f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -120,7 +120,7 @@
mTestHandler = Handler.createAsync(Looper.myLooper());
mSbn = createNewNotification(0 /* id */);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mAlertingNotificationManager = createAlertingNotificationManager();
}
@@ -171,7 +171,7 @@
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
StatusBarNotification sbn = createNewNotification(i);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- entry.row = mRow;
+ entry.setRow(mRow);
mAlertingNotificationManager.showNotification(entry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index d409e2b..b5d305d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -78,7 +78,7 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
mDelegate, mController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index aca1f90..9bed59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -227,7 +227,7 @@
null /* overrideGroupKey */,
System.currentTimeMillis());
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- entry.row = row;
+ entry.setRow(row);
entry.createIcons(mContext, sbn);
entry.channel = new NotificationChannel(
notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
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 602e613..f64e84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -95,7 +96,7 @@
private NotificationData.Entry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
- entry.row = row;
+ entry.setRow(row);
return entry;
}
@@ -108,9 +109,9 @@
NotificationData.Entry entry2 = createEntry();
// Set up the prior state to look like three top level notifications.
- mListContainer.addContainerView(entry0.row);
- mListContainer.addContainerView(entry1.row);
- mListContainer.addContainerView(entry2.row);
+ mListContainer.addContainerView(entry0.getRow());
+ mListContainer.addContainerView(entry1.getRow());
+ mListContainer.addContainerView(entry2.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
@@ -118,15 +119,15 @@
when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
- when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
- when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+ when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
+ when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
// Run updateNotifications - the view hierarchy should be reorganized.
mViewHierarchyManager.updateNotificationViews();
- verify(mListContainer).notifyGroupChildAdded(entry1.row);
- verify(mListContainer).notifyGroupChildAdded(entry2.row);
- assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+ verify(mListContainer).notifyGroupChildAdded(entry1.getRow());
+ verify(mListContainer).notifyGroupChildAdded(entry2.getRow());
+ assertTrue(Lists.newArrayList(entry0.getRow()).equals(mListContainer.mRows));
}
@Test
@@ -135,11 +136,11 @@
NotificationData.Entry entry0 = createEntry();
NotificationData.Entry entry1 = createEntry();
NotificationData.Entry entry2 = createEntry();
- entry0.row.addChildNotification(entry1.row);
- entry0.row.addChildNotification(entry2.row);
+ entry0.getRow().addChildNotification(entry1.getRow());
+ entry0.getRow().addChildNotification(entry2.getRow());
// Set up the prior state to look like one top level notification.
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1, entry2));
@@ -152,10 +153,12 @@
mViewHierarchyManager.updateNotificationViews();
verify(mListContainer).notifyGroupChildRemoved(
- entry1.row, entry0.row.getChildrenContainer());
+ entry1.getRow(), entry0.getRow().getChildrenContainer());
verify(mListContainer).notifyGroupChildRemoved(
- entry2.row, entry0.row.getChildrenContainer());
- assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+ entry2.getRow(), entry0.getRow().getChildrenContainer());
+ assertTrue(
+ Lists.newArrayList(entry0.getRow(), entry1.getRow(), entry2.getRow())
+ .equals(mListContainer.mRows));
}
@Test
@@ -163,10 +166,10 @@
// Tests two top level notifications becoming a suppressed summary and a child.
NotificationData.Entry entry0 = createEntry();
NotificationData.Entry entry1 = createEntry();
- entry0.row.addChildNotification(entry1.row);
+ entry0.getRow().addChildNotification(entry1.getRow());
// Set up the prior state to look like a top level notification.
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0, entry1));
@@ -179,23 +182,23 @@
mViewHierarchyManager.updateNotificationViews();
verify(mListContainer).notifyGroupChildRemoved(
- entry1.row, entry0.row.getChildrenContainer());
- assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
- assertEquals(View.GONE, entry0.row.getVisibility());
- assertEquals(View.VISIBLE, entry1.row.getVisibility());
+ entry1.getRow(), entry0.getRow().getChildrenContainer());
+ assertTrue(Lists.newArrayList(entry0.getRow(), entry1.getRow()).equals(mListContainer.mRows));
+ assertEquals(View.GONE, entry0.getRow().getVisibility());
+ assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
}
@Test
public void testUpdateNotificationViews_appOps() throws Exception {
NotificationData.Entry entry0 = createEntry();
- entry0.row = spy(entry0.row);
+ entry0.setRow(spy(entry0.getRow()));
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0));
- mListContainer.addContainerView(entry0.row);
+ mListContainer.addContainerView(entry0.getRow());
mViewHierarchyManager.updateNotificationViews();
- verify(entry0.row, times(1)).showAppOpsIcons(any());
+ verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
}
private class FakeListContainer implements NotificationListContainer {
@@ -252,7 +255,7 @@
public void setMaxDisplayedNotifications(int maxNotifications) {}
@Override
- public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+ public void snapViewIfNeeded(Entry entry) {}
@Override
public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
@@ -271,10 +274,10 @@
}
@Override
- public void cleanUpViewState(View view) {}
+ public void cleanUpViewStateForEntry(Entry entry) { }
@Override
- public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+ public boolean isInVisibleLocation(Entry entry) {
return true;
}
@@ -286,5 +289,6 @@
public boolean hasPulsingNotifications() {
return false;
}
+
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 1d977d8..76b992f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -16,18 +16,15 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -38,7 +35,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Before;
import org.junit.Test;
@@ -93,7 +89,7 @@
@Test
public void testSendSmartReply_updatesRemoteInput() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
// Sending smart reply should make calls to NotificationEntryManager
// to update the notification with reply and spinner.
@@ -103,11 +99,21 @@
@Test
public void testSendSmartReply_logsToStatusBar() throws RemoteException {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
// Check we log the result to the status bar service.
verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
- TEST_CHOICE_INDEX);
+ TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
+ }
+
+
+ @Test
+ public void testSendSmartReply_logsToStatusBar_generatedByAssistant() throws RemoteException {
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
+
+ // Check we log the result to the status bar service.
+ verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
+ TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
}
@Test
@@ -121,14 +127,14 @@
@Test
public void testSendSmartReply_reportsSending() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
}
@Test
public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
- mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
mSmartReplyController.stopSending(mEntry);
assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index d1fe5af..8706e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -262,14 +262,14 @@
NotificationData.Entry.class);
verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
NotificationData.Entry entry = entryCaptor.getValue();
- verify(mRemoteInputManager).bindRow(entry.row);
+ verify(mRemoteInputManager).bindRow(entry.getRow());
// Row content inflation:
verify(mCallback).onNotificationAdded(entry);
verify(mPresenter).updateNotificationViews();
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
- assertNotNull(entry.row);
+ assertNotNull(entry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@@ -294,7 +294,7 @@
verify(mPresenter).updateNotificationViews();
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
- assertNotNull(mEntry.row);
+ assertNotNull(mEntry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@@ -303,7 +303,7 @@
public void testRemoveNotification() throws Exception {
com.android.systemui.util.Assert.isNotMainThread();
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -313,7 +313,7 @@
verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
verify(mForegroundServiceController).removeNotification(mSbn);
- verify(mListContainer).cleanUpViewState(mRow);
+ verify(mListContainer).cleanUpViewStateForEntry(mEntry);
verify(mPresenter).updateNotificationViews();
verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
verify(mRow).setRemoved();
@@ -332,7 +332,7 @@
extenders.clear();
extenders.add(extender);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -347,7 +347,7 @@
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
.thenReturn(mEntry.key);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
mEntryManager.updateNotificationsForAppOp(
@@ -372,7 +372,7 @@
@Test
public void testAddNotificationExistingAppOps() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
ArraySet<Integer> expected = new ArraySet<>();
expected.add(3);
@@ -395,7 +395,7 @@
@Test
public void testAdd_noExistingAppOps() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
when(mForegroundServiceController.getStandardLayoutKey(
mEntry.notification.getUserId(),
@@ -409,7 +409,7 @@
@Test
public void testAdd_existingAppOpsNotForegroundNoti() {
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
ArraySet<Integer> ops = new ArraySet<>();
ops.add(3);
@@ -431,7 +431,7 @@
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntry.setInflationTask(mAsyncInflationTask);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -447,7 +447,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, null);
@@ -461,7 +461,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = null;
+ mEntry.setRow(null);
mEntryManager.getNotificationData().add(mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -476,7 +476,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mEntry.row = null;
+ mEntry.setRow(null);
mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index ffb1c2d..f190979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -47,7 +47,7 @@
public void setUp() {
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 1c7a8e8..3710fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -89,7 +89,7 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
+ mEntry.setRow(mRow);
mLogger = new TestableNotificationLogger(mBarService);
mLogger.setUpWithContainer(mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index a6725b8..7fee0ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,11 +16,10 @@
package com.android.systemui.statusbar.notification.row;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
@@ -106,7 +105,8 @@
mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
- // Smart replies
+ // Smart replies and actions
+ when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
mEntry = new NotificationData.Entry(mStatusBarNotification);
when(mSmartReplyConstants.isEnabled()).thenReturn(true);
@@ -169,8 +169,10 @@
private void setupAppGeneratedReplies(
CharSequence[] smartReplyTitles,
Notification.Action freeFormRemoteInputAction) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
Notification.Action action =
- new Notification.Action.Builder(null, "Test Action", null).build();
+ new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
Pair<RemoteInput, Notification.Action> remoteInputActionPair =
Pair.create(mRemoteInput, action);
@@ -190,7 +192,7 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertFalse(repliesAndActions.smartRepliesExist());
+ assertThat(repliesAndActions.smartReplies).isNull();
}
@Test
@@ -202,7 +204,9 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions).isNull();
}
@Test
@@ -218,8 +222,10 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
- assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
}
@Test
@@ -236,8 +242,9 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
- assertThat(repliesAndActions.smartActions, is(empty()));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
+ assertThat(repliesAndActions.smartActions).isNull();
}
@Test
@@ -254,8 +261,8 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(null));
- assertThat(repliesAndActions.smartActions, is(empty()));
+ assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(repliesAndActions.smartActions).isNull();
}
@Test
@@ -269,8 +276,10 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(null));
- assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+ assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(repliesAndActions.smartActions.actions)
+ .isEqualTo(mEntry.systemGeneratedSmartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
}
@Test
@@ -295,8 +304,27 @@
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
- assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
+ assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+ assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
+ assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // actions.
+ setupAppGeneratedReplies(null);
+
+ when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
+
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(repliesAndActions.smartReplies).isNull();
}
private Notification.Action.Builder createActionBuilder(String actionTitle) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 3d2ea70..2797969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -54,6 +54,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
+import android.util.Log;
import android.view.View;
import com.android.systemui.SysuiTestCase;
@@ -177,10 +178,17 @@
NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
ExpandableNotificationRow row = spy(realRow);
+
when(row.getWindowToken()).thenReturn(new Binder());
when(row.getGuts()).thenReturn(guts);
doNothing().when(row).inflateGuts();
+ NotificationData.Entry realEntry = realRow.getEntry();
+ NotificationData.Entry entry = spy(realEntry);
+
+ when(entry.getRow()).thenReturn(row);
+ when(entry.getGuts()).thenReturn(guts);
+
mGutsManager.openGuts(row, 0, 0, menuItem);
mTestableLooper.processAllMessages();
verify(guts).openControls(
@@ -190,13 +198,19 @@
anyBoolean(),
any(Runnable.class));
+ // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+ verify(row).setGutsView(any());
+
row.onDensityOrFontScaleChanged();
- mGutsManager.onDensityOrFontScaleChanged(row);
+ mGutsManager.onDensityOrFontScaleChanged(entry);
+
mTestableLooper.processAllMessages();
mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+
+ // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
verify(row, times(2)).setGutsView(any());
}
@@ -470,7 +484,7 @@
ExpandableNotificationRow row = spy(createTestNotificationRow());
doReturn(guts).when(row).getGuts();
NotificationData.Entry entry = row.getEntry();
- entry.row = row;
+ entry.setRow(row);
mGutsManager.setExposedGuts(guts);
assertTrue(mGutsManager.shouldExtendLifetime(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 1cc1c63..985827a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -134,7 +134,7 @@
.thenReturn(packageInfo);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = TEST_UID; // non-zero
- when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
applicationInfo);
final PackageInfo systemPackageInfo = new PackageInfo();
systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -206,6 +206,37 @@
}
@Test
+ public void testBindNotification_noDelegate() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(GONE, nameView.getVisibility());
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(GONE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_delegate() throws Exception {
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = 7; // non-zero
+ when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+ applicationInfo);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(VISIBLE, nameView.getVisibility());
+ assertTrue(nameView.getText().toString().contains("Other"));
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(VISIBLE, dividerView.getVisibility());
+ }
+
+ @Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 74ce5f6..e65e806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -325,7 +325,9 @@
// add notification
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- when(row.isClearable()).thenReturn(true);
+ NotificationData.Entry entry = mock(NotificationData.Entry.class);
+ when(row.getEntry()).thenReturn(entry);
+ when(entry.isClearable()).thenReturn(true);
mStackScroller.addContainerView(row);
mStackScroller.onUpdateRowStates();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 10b0d83..c99e766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -81,12 +81,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
}
@@ -95,12 +95,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertTrue(mHeadsUpAppearanceController.isShown());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertFalse(mHeadsUpAppearanceController.isShown());
}
@@ -109,12 +109,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
}
@@ -125,12 +125,12 @@
mFirst.setPinned(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
- mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
mFirst.setPinned(false);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1070795..44deb10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -97,7 +97,7 @@
@Test
public void testCanRemoveImmediately_notTopEntry() {
NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
- laterEntry.row = mRow;
+ laterEntry.setRow(mRow);
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 96c57f2..c3bc511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -149,7 +149,8 @@
Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
Entry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -166,12 +167,14 @@
Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
Entry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(true);
mGroupAlertTransferHelper.onInflationFinished(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
@@ -185,7 +188,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
NotificationData.Entry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
@@ -199,10 +203,12 @@
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(true);
mGroupAlertTransferHelper.onInflationFinished(childEntry);
- verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+ verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
+ .getContentFlag());
assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
}
@@ -212,7 +218,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -229,7 +236,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -251,7 +259,8 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+ .thenReturn(false);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 1483ae5..b0bd1fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -100,7 +100,7 @@
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
- assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
}
@Test
@@ -143,9 +143,8 @@
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry.row,
- mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
@Test
@@ -161,8 +160,7 @@
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry.row,
- mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 01f44fd4..7ad68eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -81,7 +81,7 @@
0 /* postTime */);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- entry.row = row;
+ entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
when(row.getStatusBarNotification()).thenReturn(sbn);
when(row.isInflationFlagSet(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 9e659c8..b6e3fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -181,7 +181,16 @@
public void testSendSmartReply_controllerCalled() {
setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
- verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
+ verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+ false /* generatedByAsssitant */);
+ }
+
+ @Test
+ public void testSendSmartReply_controllerCalled_generatedByAssistant() {
+ setSmartReplies(TEST_CHOICES, true);
+ mView.getChildAt(2).performClick();
+ verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+ true /* generatedByAsssitant */);
}
@Test
@@ -392,11 +401,17 @@
}
private void setSmartReplies(CharSequence[] choices) {
+ setSmartReplies(choices, false);
+ }
+
+ private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
+ SmartReplyView.SmartReplies smartReplies =
+ new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
mView.resetSmartSuggestions(mContainer);
- mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+ mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
}
private Notification.Action createAction(String actionTitle) {
@@ -415,12 +430,12 @@
private void setSmartActions(String[] actionTitles) {
mView.resetSmartSuggestions(mContainer);
- mView.addSmartActions(createActions(actionTitles));
+ mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
}
private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
setSmartReplies(choices);
- mView.addSmartActions(createActions(actionTitles));
+ mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
}
private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -453,9 +468,11 @@
// Add smart replies
Button previous = null;
+ SmartReplyView.SmartReplies smartReplies =
+ new SmartReplyView.SmartReplies(choices, null, null, false);
for (int i = 0; i < choices.length; ++i) {
- Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
- null, null, null, null);
+ Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies,
+ null, null);
current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
current.getPaddingBottom());
if (previous != null) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 17d8ea7..c56f31e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,11 +18,13 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
+import static android.util.DebugUtils.flagsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -72,9 +74,12 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.intelligence.IntelligenceManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -95,6 +100,27 @@
private static final Object sLock = AutofillManagerService.class;
+
+ /**
+ * IME supports Smart Suggestions.
+ */
+ // NOTE: must be public because of flagsToString()
+ public static final int FLAG_SMART_SUGGESTION_IME = 0x1;
+
+ /**
+ * System supports Smarts Suggestions (as a popup-window similar to standard Autofill).
+ */
+ // NOTE: must be public because of flagsToString()
+ public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
+ FLAG_SMART_SUGGESTION_IME,
+ FLAG_SMART_SUGGESTION_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SmartSuggestionMode {}
+
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -102,7 +128,6 @@
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
-
/**
* Maximum number of partitions that can be allowed in a session.
*
@@ -130,6 +155,7 @@
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
private final LocalService mLocalService = new LocalService();
+ final IntelligenceManagerInternal mIntelligenceManagerInternal;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -153,13 +179,21 @@
@GuardedBy("mLock")
private boolean mAllowInstantService;
+ /**
+ * Supported modes for Augmented Autofill Smart Suggestions.
+ */
+ @GuardedBy("mLock")
+ private int mSupportedSmartSuggestionModes;
+
public AutofillManagerService(Context context) {
super(context, UserManager.DISALLOW_AUTOFILL);
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
+ mIntelligenceManagerInternal = LocalServices.getService(IntelligenceManagerInternal.class);
setLogLevelFromSettings();
setMaxPartitionsFromSettings();
setMaxVisibleDatasetsFromSettings();
+ setSmartSuggestionEmulationFromSettings();
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -186,6 +220,9 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS), false, observer,
+ UserHandle.USER_ALL);
}
@Override // from AbstractMasterSystemService
@@ -200,6 +237,9 @@
case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
setMaxVisibleDatasetsFromSettings();
break;
+ case Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS:
+ setSmartSuggestionEmulationFromSettings();
+ break;
default:
Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
// fall through
@@ -243,6 +283,10 @@
mUi.hideAll(null);
}
+ @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+ return mSupportedSmartSuggestionModes;
+ }
+
// Called by Shell command.
void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
@@ -420,6 +464,19 @@
}
}
+ private void setSmartSuggestionEmulationFromSettings() {
+ final int flags = Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0);
+ if (sDebug) {
+ Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): "
+ + smartSuggestionFlagsToString(flags));
+ }
+
+ synchronized (mLock) {
+ mSupportedSmartSuggestionModes = flags;
+ }
+ }
+
// Called by Shell command.
void getScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
@@ -610,6 +667,10 @@
}
}
+ static String smartSuggestionFlagsToString(int flags) {
+ return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags);
+ }
+
private final class LocalService extends AutofillManagerInternal {
@Override
public void onBackKeyPressed() {
@@ -1158,6 +1219,10 @@
pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+ if (mSupportedSmartSuggestionModes != 0) {
+ pw.print("Smart Suggestion modes: ");
+ pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
+ }
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 67ccc9b..0df99d4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -73,6 +73,7 @@
import com.android.server.AbstractPerUserSystemService;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
@@ -268,8 +269,8 @@
pruneAbandonedSessionsLocked();
final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
- appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed,
- flags);
+ appCallbackToken, hasCallback, componentName, compatMode,
+ bindInstantServiceAllowed, flags);
if (newSession == null) {
return NO_SESSION;
}
@@ -823,6 +824,12 @@
return true;
}
+ @GuardedBy("mLock")
+ @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+ // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master)
+ return mMaster.getSupportedSmartSuggestionModesLocked();
+ }
+
@Override
@GuardedBy("mLock")
protected void dumpLocked(String prefix, PrintWriter pw) {
@@ -962,6 +969,9 @@
if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
session.forceRemoveSelfLocked();
}
+ else {
+ session.destroyAugmentedAutofillWindowsLocked();
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1ff1acd..8676f7f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -25,6 +25,9 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM;
+import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString;
import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
@@ -93,8 +96,11 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.server.AbstractRemoteService;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
+import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -242,6 +248,10 @@
@GuardedBy("mLock")
private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
+ @GuardedBy("mLock")
+ @Nullable
+ private AugmentedAutofillCallback mAugmentedAutofillCallback;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -2497,15 +2507,83 @@
processResponseLocked(newResponse, newClientState, 0);
}
+ @GuardedBy("mLock")
private void processNullResponseLocked(int flags) {
- if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null");
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
mService.resetLastResponse();
- // Nothing to be done, but need to notify client.
- notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
- removeSelf();
+
+ // The default autofill service cannot fullfill the request, let's check if the intelligence
+ // service can.
+ mAugmentedAutofillCallback = triggerAugmentedAutofillLocked();
+ if (mAugmentedAutofillCallback == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "canceling session " + id + " when server returned null and there is no"
+ + " AugmentedAutofill for user");
+ }
+ // Nothing to be done, but need to notify client.
+ notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
+ removeSelf();
+ } else {
+ // TODO(b/111330312, b/119638958): must set internal state so when user focus other
+ // fields it does not generate a new call to the standard autofill service (right now
+ // it does). Must also add CTS tests to exercise this scenario.
+ if (sVerbose) {
+ Slog.v(TAG, "keeping session " + id + " when server returned null but "
+ + "there is an AugmentedAutofill for user");
+ }
+ }
+ }
+
+ /**
+ * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
+ *
+ * @return callback to the Augmented Autofill service, or {@code null} if not supported.
+ */
+ // TODO(b/111330312): might need to call it in other places, like when the service returns a
+ // non-null response but without datasets (for example, just SaveInfo)
+ @GuardedBy("mLock")
+ private AugmentedAutofillCallback triggerAugmentedAutofillLocked() {
+ // Check if Smart Suggestions is supported...
+ final @SmartSuggestionMode int supportedModes = mService
+ .getSupportedSmartSuggestionModesLocked();
+ if (supportedModes == 0) return null;
+
+ // ...then if the service is set for the user
+ final IntelligenceManagerInternal intelligenceManagerInternal = mService
+ .getMaster().mIntelligenceManagerInternal;
+ if (intelligenceManagerInternal == null) return null;
+
+ // Define which mode will be used
+ final int mode;
+ if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) {
+ // TODO(b/111330312): support it :-)
+ Slog.w(TAG, "Smart Suggestions on IME not supported yet");
+ return null;
+ } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
+ mode = FLAG_SMART_SUGGESTION_SYSTEM;
+ } else {
+ Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes);
+ return null;
+ }
+
+ if (mCurrentViewId == null) {
+ Slog.w(TAG, "triggerAugmentedAutofillLocked(): no view currently focused");
+ return null;
+ }
+
+ if (sVerbose) {
+ Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId
+ + " using suggestion mode " + smartSuggestionFlagsToString(mode)
+ + " when server returned null for session " + this.id);
+ }
+
+ // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
+ // furgher AFM -> AFMS calls.
+ // TODO(b/119638958): add CTS tests
+ return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient,
+ mActivityToken, this.id, mCurrentViewId);
}
@GuardedBy("mLock")
@@ -2786,6 +2864,9 @@
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+ if (mAugmentedAutofillCallback != null) {
+ pw.print(prefix); pw.println("has AugmentedAutofillCallback");
+ }
mRemoteFillService.dump(prefix, pw);
}
@@ -2957,6 +3038,14 @@
Slog.e(TAG, "Error notifying client to finish session", e);
}
}
+ destroyAugmentedAutofillWindowsLocked();
+ }
+
+ @GuardedBy("mLock")
+ void destroyAugmentedAutofillWindowsLocked() {
+ if (mAugmentedAutofillCallback != null) {
+ mAugmentedAutofillCallback.destroy();
+ }
}
/**
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index c9f7218..6af098b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -164,18 +164,15 @@
}
/**
- * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+ * Don't write apks for system-bundled apps that are not upgraded.
*/
private boolean shouldWriteApk(
ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
- boolean isForwardLocked =
- (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
boolean isUpdatedSystemApp =
(applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
return includeApks
&& !isSharedStorage
- && !isForwardLocked
&& (!isSystemApp || isUpdatedSystemApp);
}
}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index b37888f..71d261c 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -163,6 +163,20 @@
}
/**
+ * Gets the user associated with this service.
+ */
+ public final @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * Gets the master service.
+ */
+ public final M getMaster() {
+ return mMaster;
+ }
+
+ /**
* Gets this UID of the remote service this service binds to, or {@code -1} if the service is
* disabled.
*/
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 181d7fd..73a34d6 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -205,6 +205,9 @@
protected void scheduleUnbind() {
cancelScheduledUnbind();
+ // TODO(b/111276913): implement "permanent binding"
+ // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
+ // it's not)
mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
.setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74c8023..564d35a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -894,10 +894,18 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
+ // Listen to package add and removal events for all users.
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiverAsUser(
+ mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
try {
mNMS.registerObserver(mTethering);
mNMS.registerObserver(mDataActivityObserver);
@@ -4155,6 +4163,7 @@
}
private void onUserAdded(int userId) {
+ mPermissionMonitor.onUserAdded(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
@@ -4165,6 +4174,7 @@
}
private void onUserRemoved(int userId) {
+ mPermissionMonitor.onUserRemoved(userId);
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
@@ -4174,6 +4184,22 @@
}
}
+ private void onPackageAdded(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+ return;
+ }
+ mPermissionMonitor.onPackageAdded(packageName, uid);
+ }
+
+ private void onPackageRemoved(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+ return;
+ }
+ mPermissionMonitor.onPackageRemoved(uid);
+ }
+
private void onUserUnlocked(int userId) {
synchronized (mVpns) {
// User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -4185,11 +4211,15 @@
}
}
- private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
if (userId == UserHandle.USER_NULL) return;
if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -4202,6 +4232,10 @@
onUserRemoved(userId);
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
onUserUnlocked(userId);
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, uid);
}
}
};
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 046442a..e5275e5 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
import android.Manifest;
import android.annotation.NonNull;
@@ -2990,7 +2991,7 @@
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> deadUpdateRecords = null;
- // Broadcast location or status to all listeners
+ // Broadcast location to all listeners
for (UpdateRecord r : records) {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
@@ -3049,14 +3050,19 @@
}
}
- long prevStatusUpdateTime = r.mLastStatusBroadcast;
- if ((newStatusUpdateTime > prevStatusUpdateTime) &&
- (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+ // TODO: location provider status callbacks have been disabled and deprecated, and are
+ // guarded behind this setting now. should be removed completely post-Q
+ if (Settings.Global.getInt(mContext.getContentResolver(),
+ LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
+ long prevStatusUpdateTime = r.mLastStatusBroadcast;
+ if ((newStatusUpdateTime > prevStatusUpdateTime)
+ && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
- r.mLastStatusBroadcast = newStatusUpdateTime;
- if (!receiver.callStatusChangedLocked(provider, status, extras)) {
- receiverDead = true;
- Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+ r.mLastStatusBroadcast = newStatusUpdateTime;
+ if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+ receiverDead = true;
+ Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+ }
}
}
@@ -3276,7 +3282,6 @@
// we don't leave anything dangling.
clearTestProviderEnabled(provider, opPackageName);
clearTestProviderLocation(provider, opPackageName);
- clearTestProviderStatus(provider, opPackageName);
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
@@ -3409,21 +3414,6 @@
}
@Override
- public void clearTestProviderStatus(String provider, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- MockProvider mockProvider = mMockProviders.get(provider);
- if (mockProvider == null) {
- throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
- }
- mockProvider.clearStatus();
- }
- }
-
- @Override
public PendingIntent createManageLocationPermissionIntent(String packageName,
String permission) {
Preconditions.checkNotNull(packageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e9e83c..5f9e349 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -497,6 +497,18 @@
private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
/**
+ * The number of binder proxies we need to have before we start warning and
+ * dumping debug info.
+ */
+ private static final int BINDER_PROXY_HIGH_WATERMARK = 6000;
+
+ /**
+ * Low watermark that needs to be met before we consider dumping info again,
+ * after already hitting the high watermark.
+ */
+ private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
+
+ /**
* State indicating that there is no need for any blocking for network.
*/
@VisibleForTesting
@@ -8477,7 +8489,8 @@
mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
- BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
+ BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
+ BINDER_PROXY_LOW_WATERMARK);
BinderInternal.nSetBinderProxyCountEnabled(true);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@@ -9217,11 +9230,6 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpBinderProxies(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
dumpLmkLocked(pw);
}
pw.println();
@@ -9235,6 +9243,19 @@
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
}
+ if (dumpPackage == null) {
+ // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
+ // outgoing binder calls to retrieve interface descriptors; while that is system code,
+ // there is nothing preventing an app from overriding this implementation by talking to
+ // the binder driver directly, and hang up system_server in the process. So, dump
+ // without locks held, and even then only when there is an unreasonably large number of
+ // proxies in the first place.
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
+ }
}
/**
@@ -9373,7 +9394,7 @@
cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
- dumpBinderProxies(pw);
+ dumpBinderProxies(pw, 0 /* minToDump */);
} else {
String uid = args[opti];
opti++;
@@ -9714,10 +9735,17 @@
return false;
}
- void dumpBinderProxies(PrintWriter pw) {
+ void dumpBinderProxies(PrintWriter pw, int minCountToDumpInterfaces) {
pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
- dumpBinderProxyInterfaceCounts(pw,
- " Top proxy interface names held by SYSTEM");
+ final int proxyCount = BinderProxy.getProxyCount();
+ if (proxyCount >= minCountToDumpInterfaces) {
+ dumpBinderProxyInterfaceCounts(pw,
+ "Top proxy interface names held by SYSTEM");
+ } else {
+ pw.print("Not dumping proxy interface counts because size ("
+ + Integer.toString(proxyCount) + ") looks reasonable");
+ pw.println();
+ }
dumpBinderProxiesCounts(pw,
" Counts of Binder Proxies held by SYSTEM");
}
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 4fe7d03..09df7e20 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -222,11 +222,11 @@
receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
boolean result = requestAutofillData
- ? ActivityTaskManager.getService().requestAssistContextExtras(
+ ? ActivityTaskManager.getService().requestAutofillData(this,
+ receiverExtras, topActivity, 0 /* flags */)
+ : ActivityTaskManager.getService().requestAssistContextExtras(
ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
- /* focused= */ i == 0, /* newSessionId= */ i == 0)
- : ActivityTaskManager.getService().requestAutofillData(this,
- receiverExtras, topActivity, 0 /* flags */);
+ /* focused= */ i == 0, /* newSessionId= */ i == 0);
if (result) {
mPendingDataCount++;
} else if (i == 0) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 94c94a5..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -27,10 +27,7 @@
import static android.os.Process.SYSTEM_UID;
import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -42,7 +39,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,15 +60,14 @@
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
private static final boolean DBG = true;
- private static final Boolean SYSTEM = Boolean.TRUE;
- private static final Boolean NETWORK = Boolean.FALSE;
+ protected static final Boolean SYSTEM = Boolean.TRUE;
+ protected static final Boolean NETWORK = Boolean.FALSE;
private static final int VERSION_Q = Build.VERSION_CODES.Q;
private final Context mContext;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final INetworkManagementService mNetd;
- private final BroadcastReceiver mIntentReceiver;
// Values are User IDs.
private final Set<Integer> mUsers = new HashSet<>();
@@ -85,26 +80,6 @@
mPackageManager = context.getPackageManager();
mUserManager = UserManager.get(context);
mNetd = netd;
- mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
- Uri appData = intent.getData();
- String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
- if (Intent.ACTION_USER_ADDED.equals(action)) {
- onUserAdded(user);
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(user);
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- onAppAdded(appName, appUid);
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- onAppRemoved(appUid);
- }
- }
- };
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -112,17 +87,6 @@
public synchronized void startMonitoring() {
log("Monitoring");
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_ADDED);
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
if (apps == null) {
loge("No apps");
@@ -260,7 +224,14 @@
}
}
- private synchronized void onUserAdded(int user) {
+ /**
+ * Called when a user is added. See {link #ACTION_USER_ADDED}.
+ *
+ * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+ *
+ * @hide
+ */
+ public synchronized void onUserAdded(int user) {
if (user < 0) {
loge("Invalid user in onUserAdded: " + user);
return;
@@ -272,7 +243,14 @@
update(users, mApps, true);
}
- private synchronized void onUserRemoved(int user) {
+ /**
+ * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+ *
+ * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+ *
+ * @hide
+ */
+ public synchronized void onUserRemoved(int user) {
if (user < 0) {
loge("Invalid user in onUserRemoved: " + user);
return;
@@ -284,8 +262,8 @@
update(users, mApps, false);
}
-
- private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+ @VisibleForTesting
+ protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
if (currentPermission == SYSTEM) {
return currentPermission;
}
@@ -303,33 +281,39 @@
return currentPermission;
}
- private synchronized void onAppAdded(String appName, int appUid) {
- if (TextUtils.isEmpty(appName) || appUid < 0) {
- loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
- return;
- }
-
+ /**
+ * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+ *
+ * @param packageName The name of the new package.
+ * @param uid The uid of the new package.
+ *
+ * @hide
+ */
+ public synchronized void onPackageAdded(String packageName, int uid) {
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
- if (permission != mApps.get(appUid)) {
- mApps.put(appUid, permission);
+ final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+ if (permission != mApps.get(uid)) {
+ mApps.put(uid, permission);
Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(appUid, permission);
+ apps.put(uid, permission);
update(mUsers, apps, true);
}
}
- private synchronized void onAppRemoved(int appUid) {
- if (appUid < 0) {
- loge("Invalid app in onAppRemoved: " + appUid);
- return;
- }
+ /**
+ * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+ *
+ * @param uid containing the integer uid previously assigned to the package.
+ *
+ * @hide
+ */
+ public synchronized void onPackageRemoved(int uid) {
Map<Integer, Boolean> apps = new HashMap<>();
Boolean permission = null;
- String[] packages = mPackageManager.getPackagesForUid(appUid);
+ String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
permission = highestPermissionForUid(permission, name);
@@ -341,16 +325,16 @@
}
}
}
- if (permission == mApps.get(appUid)) {
+ if (permission == mApps.get(uid)) {
// The permissions of this UID have not changed. Nothing to do.
return;
} else if (permission != null) {
- mApps.put(appUid, permission);
- apps.put(appUid, permission);
+ mApps.put(uid, permission);
+ apps.put(uid, permission);
update(mUsers, apps, true);
} else {
- mApps.remove(appUid);
- apps.put(appUid, NETWORK); // doesn't matter which permission we pick here
+ mApps.remove(uid);
+ apps.put(uid, NETWORK); // doesn't matter which permission we pick here
update(mUsers, apps, false);
}
}
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index aac83b6..6fe6324 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -19,6 +19,8 @@
import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
/**
* Intelligence Manager local system service interface.
@@ -41,4 +43,37 @@
*/
public abstract boolean sendActivityAssistData(@UserIdInt int userId,
@NonNull IBinder activityToken, @NonNull Bundle data);
+
+ /**
+ * Asks the intelligence service to provide Augmented Autofill for a given activity.
+ *
+ * @param userId user handle
+ * @param client binder used to communicate with the activity that originated this request.
+ * @param activityToken activity that originated this request.
+ * @param autofillSessionId autofill session id (must be used on {@code client} calls.
+ * @param focusedId id of the the field that triggered this request.
+ *
+ * @return {@code false} if the service cannot handle this request, {@code true} otherwise.
+ * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the
+ * service is disabled (or the activity blacklisted).
+ */
+ public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+ @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+ int autofillSessionId, @NonNull AutofillId focusedId);
+
+ /**
+ * Callback used by the Autofill Session to communicate with the Augmented Autofill service.
+ */
+ public interface AugmentedAutofillCallback {
+ // TODO(b/111330312): this method is calling when the Autofill session is destroyed, the
+ // 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
+ // new FillRequest, which in turn triggers the Augmented Autofill request again)
+ /**
+ * Destroys the Autofill UI.
+ */
+ void destroy();
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9e6e381..d5e4681 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -97,8 +97,8 @@
*
* {@hide}
*/
-public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback,
- GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends LocationProviderInterface
+ implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
private static final String TAG = "GnssLocationProvider";
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..6785964 100644
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/core/java/com/android/server/location/LocationProviderInterface.java
@@ -16,33 +16,63 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-
-import android.os.Bundle;
-import android.os.WorkSource;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* Location Manager's interface for location providers.
* @hide
*/
-public interface LocationProviderInterface {
- public String getName();
+public abstract class LocationProviderInterface {
- public void enable();
- public void disable();
- public boolean isEnabled();
- public void setRequest(ProviderRequest request, WorkSource source);
+ /** Get name. */
+ public abstract String getName();
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+ /** Enable. */
+ public abstract void enable();
- // --- deprecated (but still supported) ---
- public ProviderProperties getProperties();
- public int getStatus(Bundle extras);
- public long getStatusUpdateTime();
- public boolean sendExtraCommand(String command, Bundle extras);
+ /** Disable. */
+ public abstract void disable();
+
+ /** Is enabled. */
+ public abstract boolean isEnabled();
+
+ /** Set request. */
+ public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+ /** dump. */
+ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ /** Get properties. */
+ public abstract ProviderProperties getProperties();
+
+ /**
+ * Get status.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public int getStatus(Bundle extras) {
+ return LocationProvider.AVAILABLE;
+ }
+
+ /**
+ * Get status update time.
+ *
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
+ public long getStatusUpdateTime() {
+ return 0;
+ }
+
+ /** Send extra command. */
+ public abstract boolean sendExtraCommand(String command, Bundle extras);
}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index bb86b48..b408414 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -41,7 +41,7 @@
/**
* Proxy for ILocationProvider implementations.
*/
-public class LocationProviderProxy implements LocationProviderInterface {
+public class LocationProviderProxy extends LocationProviderInterface {
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 8578761..145aee3 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -25,31 +25,31 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
/**
* A mock location provider used by LocationManagerService to implement test providers.
*
* {@hide}
*/
-public class MockProvider implements LocationProviderInterface {
+public class MockProvider extends LocationProviderInterface {
private final String mName;
private final ProviderProperties mProperties;
private final ILocationManager mLocationManager;
private final Location mLocation;
- private final Bundle mExtras = new Bundle();
+
+ private boolean mHasLocation;
+ private boolean mEnabled;
+
private int mStatus;
private long mStatusUpdateTime;
- private boolean mHasLocation;
- private boolean mHasStatus;
- private boolean mEnabled;
+ private Bundle mExtras;
private static final String TAG = "MockProvider";
@@ -61,6 +61,10 @@
mLocationManager = locationManager;
mProperties = properties;
mLocation = new Location(name);
+
+ mStatus = LocationProvider.AVAILABLE;
+ mStatusUpdateTime = 0L;
+ mExtras = null;
}
@Override
@@ -90,13 +94,12 @@
@Override
public int getStatus(Bundle extras) {
- if (mHasStatus) {
+ if (mExtras != null) {
extras.clear();
extras.putAll(mExtras);
- return mStatus;
- } else {
- return LocationProvider.AVAILABLE;
}
+
+ return mStatus;
}
@Override
@@ -120,19 +123,14 @@
mHasLocation = false;
}
+ /**
+ * @deprecated Will be removed in a future release.
+ */
+ @Deprecated
public void setStatus(int status, Bundle extras, long updateTime) {
mStatus = status;
mStatusUpdateTime = updateTime;
- mExtras.clear();
- if (extras != null) {
- mExtras.putAll(extras);
- }
- mHasStatus = true;
- }
-
- public void clearStatus() {
- mHasStatus = false;
- mStatusUpdateTime = 0;
+ mExtras = extras;
}
@Override
@@ -145,9 +143,6 @@
pw.println(prefix + "mHasLocation=" + mHasLocation);
pw.println(prefix + "mLocation:");
mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- pw.println(prefix + "mHasStatus=" + mHasStatus);
- pw.println(prefix + "mStatus=" + mStatus);
- pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
pw.println(prefix + "mExtras=" + mExtras);
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 71bae07..99c9214 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -16,22 +16,20 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
-import android.location.LocationProvider;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* A passive location provider reports locations received from other providers
@@ -40,7 +38,7 @@
*
* {@hide}
*/
-public class PassiveProvider implements LocationProviderInterface {
+public class PassiveProvider extends LocationProviderInterface {
private static final String TAG = "PassiveProvider";
private static final ProviderProperties PROPERTIES = new ProviderProperties(
@@ -78,20 +76,6 @@
}
@Override
- public int getStatus(Bundle extras) {
- if (mReportLocation) {
- return LocationProvider.AVAILABLE;
- } else {
- return LocationProvider.TEMPORARILY_UNAVAILABLE;
- }
- }
-
- @Override
- public long getStatusUpdateTime() {
- return -1;
- }
-
- @Override
public void setRequest(ProviderRequest request, WorkSource source) {
mReportLocation = request.reportLocation;
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index decdac6..9222740 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -45,5 +45,15 @@
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
void onNotificationSmartRepliesAdded(String key, int replyCount);
- void onNotificationSmartReplySent(String key, int replyIndex);
+
+ /**
+ * Notifies a smart reply is sent.
+ *
+ * @param key the notification key
+ * @param clickedIndex the index of clicked reply
+ * @param reply the reply that is sent
+ * @param generatedByAssistant specifies is the reply generated by NAS
+ */
+ void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
+ boolean generatedByAssistant);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da29e4..6005872 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -246,6 +246,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Predicate;
/** {@hide} */
@@ -887,6 +888,7 @@
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+ mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
}
}
}
@@ -902,6 +904,7 @@
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
+ mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
}
}
}
@@ -917,7 +920,8 @@
}
@Override
- public void onNotificationSmartReplySent(String key, int replyIndex) {
+ public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
+ boolean generatedByAssistant) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
@@ -927,6 +931,8 @@
mMetricsLogger.write(logMaker);
// Treat clicking on a smart reply as a user interaction.
reportUserInteraction(r);
+ mAssistants.notifyAssistantSuggestedReplySent(
+ r.sbn, reply, generatedByAssistant);
}
}
}
@@ -4398,19 +4404,20 @@
*
* Has side effects.
*/
- private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
+ private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
NotificationRecord r, boolean isAutogroup) {
final String pkg = r.sbn.getPackageName();
final boolean isSystemNotification =
- isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
+ isUidSystemOrPhone(uid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
+ final int callingUid = Binder.getCallingUid();
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
+ && isCallerInstantApp(callingUid, userId)) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
// update notifications created by the system (e.g. a foreground service
@@ -4729,7 +4736,7 @@
mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
- mAssistants.onNotificationEnqueued(r);
+ mAssistants.onNotificationEnqueuedLocked(r);
mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
DELAY_FOR_ASSISTANT_TIME);
} else {
@@ -6521,24 +6528,28 @@
}
@VisibleForTesting
- boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
+ boolean isCallerInstantApp(int callingUid, int userId) {
// System is always allowed to act for ephemeral apps.
if (isUidSystemOrPhone(callingUid)) {
return false;
}
- mAppOps.checkPackage(callingUid, pkg);
-
try {
+ final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+ if (pkgs == null) {
+ throw new SecurityException("Unknown uid " + callingUid);
+ }
+ final String pkg = pkgs[0];
+ mAppOps.checkPackage(callingUid, pkg);
+
ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
return ai.isInstantApp();
} catch (RemoteException re) {
- throw new SecurityException("Unknown package " + pkg, re);
+ throw new SecurityException("Unknown uid " + callingUid, re);
}
-
}
private void checkCallerIsSameApp(String pkg) {
@@ -6842,69 +6853,125 @@
}
}
- public void onNotificationEnqueued(final NotificationRecord r) {
+ @GuardedBy("mNotificationLock")
+ private void onNotificationEnqueuedLocked(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
- TrimCache trimCache = new TrimCache(sbn);
-
- // There should be only one, but it's a list, so while we enforce
- // singularity elsewhere, we keep it general here, to avoid surprises.
- for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info)
- && info.isSameUser(r.getUserId());
- if (!sbnVisible) {
- continue;
- }
-
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyEnqueued(info, sbnToPost, r.getChannel());
- }
- });
- }
+ notifyAssistantLocked(
+ sbn,
+ true /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ });
}
- private void notifyEnqueued(final ManagedServiceInfo info,
- final StatusBarNotification sbn, final NotificationChannel channel) {
- final INotificationListener assistant = (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
- try {
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, channel);
- } catch (RemoteException ex) {
- Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
- }
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantExpansionChangedLocked(
+ final StatusBarNotification sbn,
+ final boolean isUserAction,
+ final boolean isExpanded) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
}
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantNotificationDirectReplyLocked(
+ final StatusBarNotification sbn) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationDirectReply(key);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantSuggestedReplySent(
+ final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onSuggestedReplySent(
+ key,
+ reply,
+ generatedByAssistant
+ ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+ : NotificationAssistantService.SOURCE_FROM_APP);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+ }
+ });
+ }
+
+
/**
* asynchronously notify the assistant that a notification has been snoozed until a
* context
*/
@GuardedBy("mNotificationLock")
- public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
- final String snoozeCriterionId) {
- TrimCache trimCache = new TrimCache(sbn);
- for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info);
- if (!sbnVisible) {
- continue;
- }
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final INotificationListener assistant =
- (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder
- = new StatusBarNotificationHolder(sbnToPost);
+ private void notifyAssistantSnoozedLocked(
+ final StatusBarNotification sbn, final String snoozeCriterionId) {
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
try {
assistant.onNotificationSnoozedUntilContext(
sbnHolder, snoozeCriterionId);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
}
- }
- });
+ });
+ }
+
+ /**
+ * Notifies the assistant something about the specified notification, only assistant
+ * that is visible to the notification will be notified.
+ *
+ * @param sbn the notification object that the update is about.
+ * @param sameUserOnly should the update be sent to the assistant in the same user only.
+ * @param callback the callback that provides the assistant to be notified, executed
+ * in WorkerHandler.
+ */
+ @GuardedBy("mNotificationLock")
+ private void notifyAssistantLocked(
+ final StatusBarNotification sbn,
+ boolean sameUserOnly,
+ BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+ TrimCache trimCache = new TrimCache(sbn);
+ // There should be only one, but it's a list, so while we enforce
+ // singularity elsewhere, we keep it general here, to avoid surprises.
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info)
+ && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
+ if (!sbnVisible) {
+ continue;
+ }
+ final INotificationListener assistant = (INotificationListener) info.service;
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ mHandler.post(() -> callback.accept(assistant, sbnHolder));
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cc640f0..093b85e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,6 +16,28 @@
package com.android.server.pm;
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
+import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -41,6 +63,8 @@
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
+import dalvik.system.DexFile;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -48,32 +72,6 @@
import java.util.List;
import java.util.Map;
-import dalvik.system.DexFile;
-
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
-
-import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
-import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
-import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
-import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
-import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
-import static com.android.server.pm.Installer.DEXOPT_FORCE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
-import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-
-import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
-
-import static dalvik.system.DexFile.getSafeModeCompilerFilter;
-import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
-
/**
* Helper class for running dexopt command on packages.
*/
@@ -544,8 +542,7 @@
// The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
// the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() &&
- (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
+ boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// Some apps are executed with restrictions on hidden API usage. If this app is one
// of them, pass a flag to dexopt to enable the same restrictions during compilation.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1a5b86c..2e9a71a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -455,12 +455,6 @@
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
- if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
- || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- throw new IllegalArgumentException(
- "New installs into ASEC containers no longer supported");
- }
-
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -487,21 +481,10 @@
if (!PackageHelper.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
-
- } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- if (!PackageHelper.fitsOnExternal(mContext, params)) {
- throw new IOException("No suitable external storage available");
- }
-
- } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
- // For now, installs to adopted media are treated as internal from
- // an install flag point-of-view.
- params.setInstallFlagsInternal();
-
} else {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
- params.setInstallFlagsInternal();
+ params.installFlags |= PackageManager.INSTALL_INTERNAL;
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index acbd81d..6038e24 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,7 +43,6 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -646,9 +645,6 @@
/** Directory where installed application's 32-bit native libraries are copied. */
private static final File sAppLib32InstallDir =
new File(Environment.getDataDirectory(), "app-lib");
- /** Directory where code and non-resource assets of forward-locked applications are stored */
- private static final File sDrmAppPrivateInstallDir =
- new File(Environment.getDataDirectory(), "app-private");
// ----------------------------------------------------------------
@@ -1811,12 +1807,10 @@
firstUserIds, firstInstantUserIds);
}
- // Send broadcast package appeared if forward locked/external for all users
- // treat asec-hosted packages like removable media on upgrade
- if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+ // Send broadcast package appeared if external for all users
+ if (isExternal(res.pkg)) {
if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg
- + " is ASEC-hosted -> AVAILABLE");
+ Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
}
final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
ArrayList<String> pkgList = new ArrayList<>(1);
@@ -2629,10 +2623,6 @@
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
- scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
- | PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
@@ -8631,7 +8621,7 @@
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
final InstallArgs args = createInstallArgsForExisting(
- packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
args.cleanUpResourcesLI();
synchronized (mPackages) {
@@ -8704,7 +8694,7 @@
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
InstallArgs args = createInstallArgsForExisting(
- packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
@@ -11536,11 +11526,8 @@
// pass once we've determined ABI below.
setNativeLibraryPaths(pkg, sAppLib32InstallDir);
- // We would never need to extract libs for forward-locked and external packages,
- // since the container service will do it for us. We shouldn't attempt to
- // extract libs from system app when it was not updated.
- if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
- (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
+ // We shouldn't attempt to extract libs from system app when it was not updated.
+ if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
extractLibs = false;
}
@@ -11881,7 +11868,6 @@
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
- final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
info.nativeLibraryRootDir = null;
info.nativeLibraryRootRequiresIsa = false;
@@ -11910,9 +11896,6 @@
info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
secondaryLibDir, apkName).getAbsolutePath();
}
- } else if (asecApp) {
- info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
- .getAbsolutePath();
} else {
final String apkName = deriveCodePathName(codePath);
info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
@@ -14021,7 +14004,6 @@
private int installLocationPolicy(PackageInfoLite pkgLite) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
- boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
// reader
synchronized (mPackages) {
// Currently installed package which the new package is attempting to replace or
@@ -14074,16 +14056,8 @@
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (onSd) {
- Slog.w(TAG, "Cannot install update to system app on sdcard");
- return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
- }
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else {
- if (onSd) {
- // Install flag overrides everything.
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
// If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
@@ -14104,11 +14078,6 @@
}
}
}
- // All the special cases have been taken care of.
- // Return result based on recommended install location.
- if (onSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
return pkgLite.recommendedInstallLocation;
}
@@ -14125,70 +14094,59 @@
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
- final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
- if (onInt && onSd) {
- // Check if both bits are set.
- Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else if (onSd && ephemeral) {
- Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else {
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
- if (DEBUG_INSTANT && ephemeral) {
- Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+
+ if (DEBUG_INSTANT && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
+ /*
+ * If we have too little free space, try to free cache
+ * before giving up.
+ */
+ if (!origin.staged && pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+ origin.resolvedPath, packageAbiOverride);
+ if (sizeBytes >= 0) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
}
/*
- * If we have too little free space, try to free cache
- * before giving up.
+ * The cache free must have deleted the file we downloaded to install.
+ *
+ * TODO: fix the "freeCache" call to not delete the file we care about.
*/
- if (!origin.staged && pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- // TODO: focus freeing disk space on the target device
- final StorageManager storage = StorageManager.from(mContext);
- final long lowThreshold = storage.getStorageLowBytes(
- Environment.getDataDirectory());
-
- final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
- origin.resolvedPath, packageAbiOverride);
- if (sizeBytes >= 0) {
- try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
- }
- }
-
- /*
- * The cache free must have deleted the file we
- * downloaded to install.
- *
- * TODO: fix the "freeCache" call to not delete
- * the file we care about.
- */
- if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- pkgLite.recommendedInstallLocation
+ if (pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+ pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
- }
}
}
+
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
@@ -14208,24 +14166,21 @@
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
- } else if (!onSd && !onInt) {
+ } else if (!onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
- installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
}
installFlags |= PackageManager.INSTALL_INSTANT_APP;
- installFlags &= ~(PackageManager.INSTALL_EXTERNAL
- |PackageManager.INSTALL_INTERNAL);
+ installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external
// media is unset
installFlags |= PackageManager.INSTALL_INTERNAL;
- installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
}
}
@@ -14409,7 +14364,7 @@
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
*/
- private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
+ private InstallArgs createInstallArgsForExisting(String codePath,
String resourcePath, String[] instructionSets) {
return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
@@ -14501,14 +14456,6 @@
return PackageManager.INSTALL_SUCCEEDED;
}
- protected boolean isFwdLocked() {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- }
-
- protected boolean isExternalAsec() {
- return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
- }
-
protected boolean isEphemeral() {
return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
}
@@ -14536,7 +14483,7 @@
}
/**
- * Logic to handle installation of non-ASEC applications, including copying
+ * Logic to handle installation of new applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
@@ -14558,9 +14505,6 @@
params.grantedRuntimePermissions,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.mParentInstallParams);
- if (isFwdLocked()) {
- throw new IllegalArgumentException("Forward locking only supported in ASEC");
- }
}
/** Existing install */
@@ -15291,7 +15235,7 @@
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
- res.removedInfo.args = createInstallArgsForExisting(0,
+ res.removedInfo.args = createInstallArgsForExisting(
oldPackage.applicationInfo.getCodePath(),
oldPackage.applicationInfo.getResourcePath(),
getAppDexInstructionSets(oldPackage.applicationInfo));
@@ -15570,8 +15514,6 @@
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
- final boolean forwardLocked =
- ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
final boolean instantApp =
((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
@@ -15604,10 +15546,8 @@
// This update happens in place!
//
// We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not forward locked.
- // 2) it is not on on an external ASEC container.
- // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 4) it is not debuggable.
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
//
// Note that we do not dexopt instant apps by default. dexopt can take some time to
// complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15615,9 +15555,8 @@
// continuous progress to the useur instead of mysteriously blocking somewhere in the
// middle of running an instant app. The default behaviour can be overridden
// via gservices.
- final boolean performDexopt = !forwardLocked
- && !pkg.applicationInfo.isExternalAsec()
- && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ final boolean performDexopt =
+ (!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
@@ -15712,7 +15651,6 @@
// Parse old package
boolean oldExternal = isExternal(oldPackage);
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
- | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
@@ -15830,9 +15768,7 @@
final String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
final File tmpPackageFile = new File(args.getCodePath());
- final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
- final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
- || (args.volumeUuid != null));
+ final boolean onExternal = args.volumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
@@ -15859,16 +15795,14 @@
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Sanity check
- if (instantApp && (forwardLocked || onExternal)) {
- Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
- + " external=" + onExternal);
+ if (instantApp && onExternal) {
+ Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
// Retrieve PackageSettings and parse package
@ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE
- | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
@@ -16213,7 +16147,7 @@
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
- } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+ } else {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
@@ -16745,19 +16679,6 @@
return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
- private int packageFlagsToInstallFlags(PackageSetting ps) {
- int installFlags = 0;
- if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
- // This existing package was an external ASEC install when we have
- // the external flag without a UUID
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- }
- if (ps.isForwardLocked()) {
- installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- }
- return installFlags;
- }
-
private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -16773,9 +16694,6 @@
private void deleteTempPackageFiles() {
final FilenameFilter filter =
(dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
- for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
- file.delete();
- }
}
@Override
@@ -17766,7 +17684,7 @@
// Delete application code and resources only for parent packages
if (ps.parentPackageName == null) {
if (deleteCodeAndResources && (outInfo != null)) {
- outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+ outInfo.args = createInstallArgsForExisting(
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
@@ -21698,7 +21616,6 @@
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final PackageManager pm = mContext.getPackageManager();
- final boolean currentAsec;
final String currentVolumeUuid;
final File codeFile;
final String installerPackageName;
@@ -21732,22 +21649,13 @@
"3rd party apps are not allowed on internal storage");
}
- if (pkg.applicationInfo.isExternalAsec()) {
- currentAsec = true;
- currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
- } else if (pkg.applicationInfo.isForwardLocked()) {
- currentAsec = true;
- currentVolumeUuid = "forward_locked";
- } else {
- currentAsec = false;
- currentVolumeUuid = ps.volumeUuid;
+ currentVolumeUuid = ps.volumeUuid;
- final File probe = new File(pkg.codePath);
- final File probeOat = new File(probe, "oat");
- if (!probe.isDirectory() || !probeOat.isDirectory()) {
- throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
- "Move only supported for modern cluster style installs");
- }
+ final File probe = new File(pkg.codePath);
+ final File probeOat = new File(probe, "oat");
+ if (!probe.isDirectory() || !probeOat.isDirectory()) {
+ throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+ "Move only supported for modern cluster style installs");
}
if (Objects.equals(currentVolumeUuid, volumeUuid)) {
@@ -21784,12 +21692,11 @@
final boolean moveCompleteApp;
final File measurePath;
+ installFlags = INSTALL_INTERNAL;
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
- installFlags = INSTALL_INTERNAL;
- moveCompleteApp = !currentAsec;
+ moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
} else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
- installFlags = INSTALL_EXTERNAL;
moveCompleteApp = false;
measurePath = storage.getPrimaryPhysicalVolume().getPath();
} else {
@@ -21801,9 +21708,6 @@
"Move location not mounted private volume");
}
- Preconditions.checkState(!currentAsec);
-
- installFlags = INSTALL_INTERNAL;
moveCompleteApp = true;
measurePath = Environment.getDataAppDirectory(volumeUuid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6f275ec..77f8c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2193,9 +2193,6 @@
boolean replaceExisting = true;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "-l":
- sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- break;
case "-r": // ignore
break;
case "-R":
@@ -2210,9 +2207,6 @@
case "-t":
sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
break;
- case "-s":
- sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
- break;
case "-f":
sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
break;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b850613..2c2cc7e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,10 +152,6 @@
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
}
- public boolean isForwardLocked() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
- }
-
public boolean isSystem() {
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 239dc99..fbf5439 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,7 +64,6 @@
| ApplicationInfo.PRIVATE_FLAG_VENDOR
| ApplicationInfo.PRIVATE_FLAG_PRODUCT
| ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
- | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
| ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6009bd3..c334b6e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3656,7 +3656,6 @@
private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
- private static int PRE_M_APP_INFO_FLAG_FORWARD_LOCK = 1<<29;
private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -3756,15 +3755,11 @@
if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
}
- if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
- pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
- }
if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
| PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
- | PRE_M_APP_INFO_FLAG_FORWARD_LOCK
| PRE_M_APP_INFO_FLAG_PRIVILEGED);
} else {
// For backward compatibility
@@ -4428,7 +4423,6 @@
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
- ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 774134c..21cc14e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -195,7 +195,6 @@
// STOPSHIP(b/112545973): remove once feature enabled by default
if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
- MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_AUDIO);
}
}
@@ -203,10 +202,8 @@
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_IMAGES);
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
- MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_VIDEO);
+ MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 3179ce9..aa11e1e 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -51,6 +51,7 @@
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -1517,6 +1518,21 @@
pulledData.add(e);
}
+ private void pullBuildInformation(int tagId,
+ long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(Build.FINGERPRINT);
+ e.writeString(Build.BRAND);
+ e.writeString(Build.PRODUCT);
+ e.writeString(Build.DEVICE);
+ e.writeString(Build.VERSION.RELEASE);
+ e.writeString(Build.ID);
+ e.writeString(Build.VERSION.INCREMENTAL);
+ e.writeString(Build.TYPE);
+ e.writeString(Build.TAGS);
+ pulledData.add(e);
+ }
+
private BatteryStatsHelper getBatteryStatsHelper() {
if (mBatteryStatsHelper == null) {
final long callingToken = Binder.clearCallingIdentity();
@@ -1810,6 +1826,10 @@
pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.BUILD_INFORMATION: {
+ pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.PROCESS_CPU_TIME: {
pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 1eb44a0..361622f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1177,12 +1177,14 @@
}
@Override
- public void onNotificationSmartReplySent(String key, int replyIndex)
+ public void onNotificationSmartReplySent(
+ String key, int replyIndex, CharSequence reply, boolean generatedByAssistant)
throws RemoteException {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+ mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
+ generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 409d2b4..6ede423 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -88,7 +88,6 @@
import android.util.Xml;
import android.view.Display;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
@@ -487,6 +486,8 @@
private void generateCrop(WallpaperData wallpaper) {
boolean success = false;
+ // Only generate crop for default display.
+ final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
Rect cropHint = new Rect(wallpaper.cropHint);
if (DEBUG) {
@@ -494,7 +495,7 @@
+ Integer.toHexString(wallpaper.whichPending)
+ " to " + wallpaper.cropFile.getName()
+ " crop=(" + cropHint.width() + 'x' + cropHint.height()
- + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
+ + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
}
// Analyse the source; needed in multiple cases
@@ -533,11 +534,11 @@
}
// scale if the crop height winds up not matching the recommended metrics
- needScale = (wallpaper.height != cropHint.height());
+ needScale = (wpData.mHeight != cropHint.height());
if (DEBUG) {
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
- Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+ Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
}
@@ -567,7 +568,7 @@
// just let the decode take care of it because we also want to remap where the
// cropHint rectangle lies in the decoded [super]rect.
final BitmapFactory.Options scaler;
- final int actualScale = cropHint.height() / wallpaper.height;
+ final int actualScale = cropHint.height() / wpData.mHeight;
int scale = 1;
while (2*scale < actualScale) {
scale *= 2;
@@ -593,17 +594,18 @@
cropHint.offsetTo(0, 0);
cropHint.right /= scale; // adjust by downsampling factor
cropHint.bottom /= scale;
- final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+ final float heightR =
+ ((float) wpData.mHeight) / ((float) cropHint.height());
if (DEBUG) {
Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
}
final int destWidth = (int)(cropHint.width() * heightR);
final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
- destWidth, wallpaper.height, true);
+ destWidth, wpData.mHeight, true);
if (DEBUG) {
Slog.v(TAG, "Final extract:");
- Slog.v(TAG, " dims: w=" + wallpaper.width
- + " h=" + wallpaper.height);
+ Slog.v(TAG, " dims: w=" + wpData.mWidth
+ + " h=" + wpData.mHeight);
Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ " h=" + finalCrop.getHeight());
}
@@ -670,13 +672,13 @@
if (connector == null) return;
connector.disconnectLocked();
mLastWallpaper.connection.removeDisplayConnector(displayId);
+ mLastWallpaper.removeDisplayData(displayId);
}
}
}
@Override
public void onDisplayChanged(int displayId) {
- // TODO(b/115486823) Review that do we need to handle display changes.
}
};
@@ -778,16 +780,23 @@
private RemoteCallbackList<IWallpaperManagerCallback> callbacks
= new RemoteCallbackList<IWallpaperManagerCallback>();
- int width = -1;
- int height = -1;
+ private static final class DisplayData {
+ int mWidth = -1;
+ int mHeight = -1;
+ final Rect mPadding = new Rect(0, 0, 0, 0);
+ final int mDisplayId;
+
+ DisplayData(int displayId) {
+ mDisplayId = displayId;
+ }
+ }
+ private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
/**
* The crop hint supplied for displaying a subset of the source image
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
- final Rect padding = new Rect(0, 0, 0, 0);
-
WallpaperData(int userId, String inputFileName, String cropFileName) {
this.userId = userId;
final File wallpaperDir = getWallpaperDir(userId);
@@ -803,6 +812,44 @@
boolean sourceExists() {
return wallpaperFile.exists();
}
+
+ void removeDisplayData(int displayId) {
+ mDisplayDatas.remove(displayId);
+ }
+ }
+
+ private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
+ WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+ if (wpdData == null) {
+ wpdData = new WallpaperData.DisplayData(displayId);
+ ensureSaneWallpaperDisplaySize(wpdData, displayId);
+ data.mDisplayDatas.append(displayId, wpdData);
+ }
+ return wpdData;
+ }
+
+ private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
+ int displayId) {
+ // We always want to have some reasonable width hint.
+ final int baseSize = getMaximumSizeDimension(displayId);
+ if (wpdData.mWidth < baseSize) {
+ wpdData.mWidth = baseSize;
+ }
+ if (wpdData.mHeight < baseSize) {
+ wpdData.mHeight = baseSize;
+ }
+ }
+
+ private int getMaximumSizeDimension(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ return display.getMaximumSizeDimension();
+ }
+
+ void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
+ for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
+ final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+ action.accept(wpdData);
+ }
}
int makeWallpaperIdLocked() {
@@ -830,9 +877,11 @@
}
void ensureStatusHandled() {
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
+ mDisplayId);
if (mDimensionsChanged) {
try {
- mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+ mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set wallpaper dimensions", e);
}
@@ -840,7 +889,7 @@
}
if (mPaddingChanged) {
try {
- mEngine.setDisplayPadding(mWallpaper.padding);
+ mEngine.setDisplayPadding(wpdData.mPadding);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set wallpaper padding", e);
}
@@ -857,16 +906,16 @@
return;
}
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ mDisplayId);
try {
- // TODO(b/115486823) Consider the size of non-default display
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
- wallpaper.width, wallpaper.height,
- wallpaper.padding, mDisplayId);
+ wpdData.mWidth, wpdData.mHeight,
+ wpdData.mPadding, mDisplayId);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
- // TODO(b/115486823) Failed when attaching a new engine, however, other engines
- // may still working. Should we abandon them all or just ignore this one.
- if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+ if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+ && connection.getConnectedEngineSize() == 0) {
bindWallpaperComponentLocked(null /* componentName */, false /* force */,
false /* fromUser */, wallpaper, null /* reply */);
}
@@ -952,11 +1001,20 @@
void forEachDisplayConnector(Consumer<DisplayConnector> action) {
for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
- final DisplayConnector connector = mDisplayConnector.get(i);
+ final DisplayConnector connector = mDisplayConnector.valueAt(i);
action.accept(connector);
}
}
+ int getConnectedEngineSize() {
+ int engineSize = 0;
+ for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+ final DisplayConnector connector = mDisplayConnector.valueAt(i);
+ if (connector.mEngine != null) engineSize++;
+ }
+ return engineSize;
+ }
+
DisplayConnector getDisplayConnectorOrCreate(int displayId) {
DisplayConnector connector = mDisplayConnector.get(displayId);
if (connector == null) {
@@ -1128,7 +1186,8 @@
Slog.w(TAG, "Failed to set ambient mode state", e);
}
}
- // TODO(b/115486823) Extends for secondary display.
+ // TODO(multi-display) So far, we have shared the same wallpaper on each display.
+ // Once we have multiple wallpapers on multiple displays, please complete here.
if (displayId == DEFAULT_DISPLAY) {
try {
// This will trigger onComputeColors in the wallpaper engine.
@@ -1560,7 +1619,7 @@
wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
final WallpaperData fallback = new WallpaperData(wallpaper.userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
- ensureSaneWallpaperData(fallback);
+ ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
mWaitingForUnlock = true;
}
@@ -1705,8 +1764,15 @@
return false;
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setDimensionHints(int width, int height, String callingPackage)
+ private boolean isValidDisplay(int displayId) {
+ return mDisplayManager.getDisplay(displayId) != null;
+ }
+
+ /**
+ * Sets the dimension hint for the wallpaper. These hints indicate the desired
+ * minimum width and height for the wallpaper in a particular display.
+ */
+ public void setDimensionHints(int width, int height, String callingPackage, int displayId)
throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
@@ -1719,90 +1785,113 @@
throw new IllegalArgumentException("width and height must be > 0");
}
- if (width != wallpaper.width || height != wallpaper.height) {
- wallpaper.width = width;
- wallpaper.height = height;
- saveSettingsLocked(userId);
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
+
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ if (width != wpdData.mWidth || height != wpdData.mHeight) {
+ wpdData.mWidth = width;
+ wpdData.mHeight = height;
+ if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- // TODO(b/115486823) Extends this method with specific display.
- final IWallpaperEngine engine = wallpaper.connection
- .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
if (engine != null) {
try {
engine.setDesiredSize(width, height);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
- } else if (wallpaper.connection.mService != null) {
+ } else if (wallpaper.connection.mService != null && connector != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- // TODO(b/115486823) Extends this method with specific display.
- wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
- .mDimensionsChanged = true;
+ connector.mDimensionsChanged = true;
}
}
}
}
}
- public int getWidthHint() throws RemoteException {
+ /**
+ * Returns the desired minimum width for the wallpaper in a particular display.
+ */
+ public int getWidthHint(int displayId) throws RemoteException {
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- return wallpaper.width;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ displayId);
+ return wpdData.mWidth;
} else {
return 0;
}
}
}
- public int getHeightHint() throws RemoteException {
+ /**
+ * Returns the desired minimum height for the wallpaper in a particular display.
+ */
+ public int getHeightHint(int displayId) throws RemoteException {
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
if (wallpaper != null) {
- return wallpaper.height;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ displayId);
+ return wpdData.mHeight;
} else {
return 0;
}
}
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setDisplayPadding(Rect padding, String callingPackage) {
+ /**
+ * Sets extra padding that we would like the wallpaper to have outside of the display.
+ */
+ public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
return;
}
synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
throw new IllegalArgumentException("padding must be positive: " + padding);
}
- if (!padding.equals(wallpaper.padding)) {
- wallpaper.padding.set(padding);
- saveSettingsLocked(userId);
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+ if (!padding.equals(wpdData.mPadding)) {
+ wpdData.mPadding.set(padding);
+ if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- // TODO(b/115486823) Extends this method with specific display.
- final IWallpaperEngine engine = wallpaper.connection
- .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
if (engine != null) {
try {
engine.setDisplayPadding(padding);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
- } else if (wallpaper.connection.mService != null) {
+ } else if (wallpaper.connection.mService != null && connector != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- // TODO(b/115486823) Extends this method with specific display.
- wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
- .mPaddingChanged = true;
+ connector.mPaddingChanged = true;
}
}
}
@@ -1850,10 +1939,13 @@
// user switch)
return null;
}
+ // Only for default display.
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
try {
if (outParams != null) {
- outParams.putInt("width", wallpaper.width);
- outParams.putInt("height", wallpaper.height);
+ outParams.putInt("width", wpdData.mWidth);
+ outParams.putInt("height", wpdData.mHeight);
}
if (cb != null) {
wallpaper.callbacks.register(cb);
@@ -2075,10 +2167,14 @@
// We know a-priori that there is no lock-only wallpaper currently
WallpaperData lockWP = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+ final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
+ DEFAULT_DISPLAY);
+ final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
+ DEFAULT_DISPLAY);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
- lockWP.width = sysWP.width;
- lockWP.height = sysWP.height;
+ lockWPDData.mWidth = sysWPDData.mWidth;
+ lockWPDData.mHeight = sysWPDData.mHeight;
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
@@ -2478,27 +2574,29 @@
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
- out.attribute(null, "width", Integer.toString(wallpaper.width));
- out.attribute(null, "height", Integer.toString(wallpaper.height));
+ out.attribute(null, "width", Integer.toString(wpdData.mWidth));
+ out.attribute(null, "height", Integer.toString(wpdData.mHeight));
out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
- if (wallpaper.padding.left != 0) {
- out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+ if (wpdData.mPadding.left != 0) {
+ out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
}
- if (wallpaper.padding.top != 0) {
- out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+ if (wpdData.mPadding.top != 0) {
+ out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
}
- if (wallpaper.padding.right != 0) {
- out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+ if (wpdData.mPadding.right != 0) {
+ out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
}
- if (wallpaper.padding.bottom != 0) {
- out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+ if (wpdData.mPadding.bottom != 0) {
+ out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
}
if (wallpaper.primaryColors != null) {
@@ -2601,14 +2699,14 @@
wallpaper = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
} else {
// sanity fallback: we're in bad shape, but establishing a known
// valid system+lock WallpaperData will keep us from dying.
Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
mWallpaperMap.put(userId, wallpaper);
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
}
}
}
@@ -2637,6 +2735,8 @@
}
}
boolean success = false;
+ final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+ DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
@@ -2663,8 +2763,8 @@
}
if (DEBUG) {
- Slog.v(TAG, "mWidth:" + wallpaper.width);
- Slog.v(TAG, "mHeight:" + wallpaper.height);
+ Slog.v(TAG, "mWidth:" + wpdData.mWidth);
+ Slog.v(TAG, "mHeight:" + wpdData.mHeight);
Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
Slog.v(TAG, "mName:" + wallpaper.name);
@@ -2700,10 +2800,10 @@
IoUtils.closeQuietly(stream);
if (!success) {
- wallpaper.width = -1;
- wallpaper.height = -1;
+ wpdData.mWidth = -1;
+ wpdData.mHeight = -1;
wallpaper.cropHint.set(0, 0, 0, 0);
- wallpaper.padding.set(0, 0, 0, 0);
+ wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
mLockWallpaperMap.remove(userId);
@@ -2717,26 +2817,22 @@
}
}
- ensureSaneWallpaperData(wallpaper);
+ ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
- ensureSaneWallpaperData(lockWallpaper);
+ ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
}
}
- private void ensureSaneWallpaperData(WallpaperData wallpaper) {
- // We always want to have some reasonable width hint.
- int baseSize = getMaximumSizeDimension();
- if (wallpaper.width < baseSize) {
- wallpaper.width = baseSize;
- }
- if (wallpaper.height < baseSize) {
- wallpaper.height = baseSize;
- }
- // and crop, if not previously specified
- if (wallpaper.cropHint.width() <= 0
- || wallpaper.cropHint.height() <= 0) {
- wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
+ private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
+ final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+
+ if (displayId == DEFAULT_DISPLAY) {
+ // crop, if not previously specified
+ if (wallpaper.cropHint.width() <= 0
+ || wallpaper.cropHint.height() <= 0) {
+ wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
+ }
}
}
@@ -2752,19 +2848,20 @@
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
+ final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+
if (!keepDimensionHints) {
- wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
- wallpaper.height = Integer.parseInt(parser
- .getAttributeValue(null, "height"));
+ wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+ wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
}
wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
- wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
- wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
- wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
- wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+ wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
+ wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
+ wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
+ wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
if (colorsCount > 0) {
Color primary = null, secondary = null, tertiary = null;
@@ -2787,12 +2884,6 @@
wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
}
- private int getMaximumSizeDimension() {
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- Display d = wm.getDefaultDisplay();
- return d.getMaximumSizeDimension();
- }
-
// Called by SystemBackupAgent after files are restored to disk.
public void settingsRestored() {
// Verify caller is the system
@@ -2832,7 +2923,7 @@
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ " id=" + wallpaper.wallpaperId);
if (success) {
- generateCrop(wallpaper); // based on the new image + metadata
+ generateCrop(wallpaper); // based on the new image + metadata
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
wallpaper, null);
}
@@ -2937,12 +3028,16 @@
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
pw.print(": id="); pw.println(wallpaper.wallpaperId);
- pw.print(" mWidth=");
- pw.print(wallpaper.width);
- pw.print(" mHeight=");
- pw.println(wallpaper.height);
+ forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(" displayId=");
+ pw.println(wpSize.mDisplayId);
+ pw.print(" mWidth=");
+ pw.print(wpSize.mWidth);
+ pw.print(" mHeight=");
+ pw.println(wpSize.mHeight);
+ pw.print(" mPadding="); pw.println(wpSize.mPadding);
+ });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
- pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
@@ -2973,11 +3068,15 @@
for (int i = 0; i < mLockWallpaperMap.size(); i++) {
WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
pw.print(" User "); pw.print(wallpaper.userId);
- pw.print(": id="); pw.println(wallpaper.wallpaperId);
- pw.print(" mWidth="); pw.print(wallpaper.width);
- pw.print(" mHeight="); pw.println(wallpaper.height);
+ pw.print(": id="); pw.println(wallpaper.wallpaperId);
+ forEachDisplayData(wallpaper, wpSize -> {
+ pw.print(" displayId=");
+ pw.println(wpSize.mDisplayId);
+ pw.print(" mWidth="); pw.print(wpSize.mWidth);
+ pw.print(" mHeight="); pw.println(wpSize.mHeight);
+ pw.print(" mPadding="); pw.println(wpSize.mPadding);
+ });
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
- pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 694e9d1..c517bd7 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -315,7 +315,7 @@
/** The number of distinct task ids that can be assigned to the tasks of a single user */
private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
- ActivityTaskManagerService mService;
+ final ActivityTaskManagerService mService;
/** The historial list of recent tasks including inactive tasks */
RecentTasks mRecentTasks;
@@ -618,11 +618,6 @@
}
@VisibleForTesting
- void setService(ActivityTaskManagerService service) {
- mService = service;
- }
-
- @VisibleForTesting
void setWindowContainerController(RootWindowContainerController controller) {
mWindowContainerController = controller;
}
@@ -1039,11 +1034,8 @@
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isTopDisplayFocusedStack(stack)) {
- continue;
- }
+ final ActivityStack stack = display.getFocusedStack();
+ if (stack != null) {
stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
final ActivityRecord top = stack.topRunningActivityLocked();
final int size = mTmpActivityList.size();
@@ -2429,6 +2421,7 @@
// We give preference to the launch preference in activity options.
if (options != null) {
taskId = options.getLaunchTaskId();
+ displayId = options.getLaunchDisplayId();
}
// First preference for stack goes to the task Id set in the activity options. Use the stack
@@ -2448,7 +2441,7 @@
T stack;
// Next preference for stack goes to the display Id set the candidate display.
- if (launchParams != null) {
+ if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
displayId = launchParams.mPreferredDisplayId;
}
if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
@@ -2566,10 +2559,10 @@
windowingMode = options != null ? options.getLaunchWindowingMode()
: r.getWindowingMode();
}
- return activityDisplay.createStack(
- windowingMode,
- options != null ? options.getLaunchActivityType() : r.getActivityType(),
- true /*onTop*/);
+ final int activityType =
+ options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
+ ? options.getLaunchActivityType() : r.getActivityType();
+ return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
}
Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d4c1bca..90f3ff8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1859,7 +1859,8 @@
}
}
- if (mStartActivity.isActivityTypeHome() && intentActivity != null
+ if (intentActivity != null
+ && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
&& intentActivity.getDisplayId() != mPreferredDisplayId) {
// Do not reuse home activity on other displays.
intentActivity = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4f01d699..d0e3fb4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -341,7 +341,8 @@
ActivityManagerInternal mAmInternal;
UriGrantsManagerInternal mUgmInternal;
private PackageManagerInternal mPmInternal;
- private ActivityTaskManagerInternal mInternal;
+ @VisibleForTesting
+ final ActivityTaskManagerInternal mInternal;
PowerManagerInternal mPowerManagerInternal;
private UsageStatsManagerInternal mUsageStatsInternal;
@@ -643,6 +644,7 @@
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
+ mInternal = new LocalService();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
}
@@ -893,7 +895,6 @@
}
private void start() {
- mInternal = new LocalService();
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@@ -6871,4 +6872,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 92944a0..52c78ce 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1081,8 +1081,12 @@
super.onDisplayChanged(dc);
if (prevDc != null && prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
- if (dc.getTopStack().getTopChild().getTopChild() == this) {
- dc.setFocusedApp(this);
+ final TaskStack stack = dc.getTopStack();
+ if (stack != null) {
+ final Task task = stack.getTopChild();
+ if (task != null && task.getTopChild() == this) {
+ dc.setFocusedApp(this);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 886b2ff..3acacbc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -352,6 +352,14 @@
int pendingLayoutChanges;
int mDeferredRotationPauseCount;
+ /**
+ * Used to gate application window layout until we have sent the complete configuration.
+ * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+ * we want to replace this flag with a mechanism that will confirm the configuration
+ * applied by the client is the one expected by the system server.
+ */
+ boolean mWaitingForConfig;
+
// TODO(multi-display): remove some of the usages.
@VisibleForTesting
boolean isDefaultDisplay;
@@ -1284,7 +1292,7 @@
+ (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
- mService.mWaitingForConfig = true;
+ mWaitingForConfig = true;
}
mRotation = rotation;
@@ -3675,6 +3683,7 @@
}
mTmpWindow = w;
w.setDisplayLayoutNeeded();
+ w.finishSeamlessRotation(true /* timeout */);
mService.markForSeamlessRotation(w, false);
}, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
* Removing the transform and the result of the {@link WindowState} layout are both tied to the
* {@link WindowState} next frame, such that they apply at the same time the client draws the
* window in the new orientation.
+ *
+ * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+ * it.
*/
- public void finish(WindowState win) {
+ public void finish(WindowState win, boolean timeout) {
mTransform.reset();
final Transaction t = win.getPendingTransaction();
t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
- if (win.mWinAnimator.mSurfaceController != null) {
+ if (win.mWinAnimator.mSurfaceController != null && !timeout) {
t.deferTransactionUntil(win.mSurfaceControl,
win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39a8465..25f3128 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -599,7 +599,6 @@
long mDisplayFreezeTime = 0;
int mLastDisplayFreezeDuration = 0;
Object mLastFinishedFreezeSource = null;
- boolean mWaitingForConfig = false;
boolean mSwitchingUser = false;
final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -1870,7 +1869,7 @@
long origId = Binder.clearCallingIdentity();
final int displayId;
synchronized (mGlobalLock) {
- WindowState win = windowForClientLocked(session, client, false);
+ final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
@@ -1885,8 +1884,9 @@
win.setFrameNumber(frameNumber);
- if (!mWaitingForConfig) {
- win.finishSeamlessRotation();
+ final DisplayContent dc = win.getDisplayContent();
+ if (!dc.mWaitingForConfig) {
+ win.finishSeamlessRotation(false /* timeout */);
}
int attrChanges = 0;
@@ -2438,7 +2438,7 @@
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
displayContent.computeScreenConfiguration(mTempConfiguration);
if (currentConfig.diff(mTempConfiguration) != 0) {
- mWaitingForConfig = true;
+ displayContent.mWaitingForConfig = true;
displayContent.setLayoutNeeded();
int anim[] = new int[2];
displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
@@ -2451,9 +2451,10 @@
return config;
}
- void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
- if (mWaitingForConfig) {
- mWaitingForConfig = false;
+ void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+ @NonNull DisplayContent dc) {
+ if (dc.mWaitingForConfig) {
+ dc.mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
@@ -4181,13 +4182,11 @@
// placement to unfreeze the display since we froze it when the rotation was updated
// in DisplayContent#updateRotationUnchecked.
synchronized (mGlobalLock) {
- if (mWaitingForConfig) {
- mWaitingForConfig = false;
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc != null && dc.mWaitingForConfig) {
+ dc.mWaitingForConfig = false;
mLastFinishedFreezeSource = "config-unchanged";
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc != null) {
- dc.setLayoutNeeded();
- }
+ dc.setLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -5136,7 +5135,7 @@
configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
if (configChanged) {
- mWaitingForConfig = true;
+ displayContent.mWaitingForConfig = true;
startFreezingDisplayLocked(0 /* exitAnim */,
0 /* enterAnim */, displayContent);
displayContent.sendNewConfiguration();
@@ -5396,23 +5395,24 @@
return;
}
- final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
- if (mWaitingForConfig || mAppsFreezingScreen > 0
+ final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+ final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+ final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+ if (waitingForConfig || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
- || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+ || mClientFreezingScreen || numOpeningApps > 0) {
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
- "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+ "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
+ ", mAppsFreezingScreen=" + mAppsFreezingScreen
+ ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
+ ", mClientFreezingScreen=" + mClientFreezingScreen
- + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+ + ", mOpeningApps.size()=" + numOpeningApps);
return;
}
if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
"stopFreezingDisplayLocked: Unfreezing now");
- final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
// We must make a local copy of the displayId as it can be potentially overwritten later on
// in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -6033,7 +6033,6 @@
pw.print(" windows="); pw.print(mWindowsFreezingScreen);
pw.print(" client="); pw.print(mClientFreezingScreen);
pw.print(" apps="); pw.print(mAppsFreezingScreen);
- pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
pw.print(" mRotation="); pw.print(defaultDisplayContent.getRotation());
pw.print(" mAltOrientation=");
@@ -6042,6 +6041,9 @@
pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
pw.print(" mLastOrientation=");
pw.println(defaultDisplayContent.getLastOrientation());
+ pw.print(" waitingForConfig=");
+ pw.println(defaultDisplayContent.mWaitingForConfig);
+
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 567b583..6f044f3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -180,6 +180,7 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -579,8 +580,8 @@
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
- void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
- boolean requested) {
+ void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+ @Rotation int rotation, boolean requested) {
// Invisible windows and the wallpaper do not participate in the seamless rotation animation
if (!isVisibleNow() || mIsWallpaper) {
return;
@@ -597,9 +598,9 @@
}
}
- void finishSeamlessRotation() {
+ void finishSeamlessRotation(boolean timeout) {
if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.finish(this);
+ mPendingSeamlessRotate.finish(this, timeout);
mFinishSeamlessRotateFrameNumber = getFrameNumber();
mPendingSeamlessRotate = null;
mService.markForSeamlessRotation(this, false);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7193dd7..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -113,7 +113,9 @@
return;
}
- if (mService.mWaitingForConfig) {
+ // TODO(multi-display):
+ final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+ if (defaultDisplay.mWaitingForConfig) {
// Our configuration has changed (most likely rotation), but we
// don't yet have the complete configuration to report to
// applications. Don't do any window layout until we have it.
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 57e954f..08fbf55 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -24,11 +24,14 @@
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.SnapshotData;
import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.AbstractRemoteService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
import java.io.PrintWriter;
@@ -39,12 +42,12 @@
private static final String TAG = "ContentCaptureSession";
private final Object mLock;
- private final IBinder mActivityToken;
-
+ final IBinder mActivityToken;
private final IntelligencePerUserService mService;
private final RemoteIntelligenceService mRemoteService;
private final InteractionContext mInterationContext;
private final InteractionSessionId mId;
+ private AugmentedAutofillCallback mAutofillCallback;
ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
@NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
@@ -92,6 +95,18 @@
}
/**
+ * Requests the service to autofill the given field.
+ */
+ public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId);
+ if (mAutofillCallback == null) {
+ mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId);
+ }
+ return mAutofillCallback;
+ }
+
+ /**
* Cleans up the session and removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
@@ -119,6 +134,11 @@
if (mService.isVerbose()) {
Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
}
+ if (mAutofillCallback != null) {
+ mAutofillCallback.destroy();
+ mAutofillCallback = null;
+ }
+
// TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
if (notifyRemoteService) {
mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
@@ -152,6 +172,8 @@
pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println();
pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println();
pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+ pw.print(prefix); pw.print("has autofill callback: ");
+ pw.println(mAutofillCallback != null);
}
@Override
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index a7f45ee..38810dd 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -27,6 +27,8 @@
import android.os.IBinder;
import android.os.UserManager;
import android.service.intelligence.InteractionSessionId;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
import android.view.intelligence.IIntelligenceManager;
@@ -86,20 +88,6 @@
service.destroyLocked();
}
- /**
- * Notifies the intelligence service of new assist data for the given activity.
- *
- * @return {@code false} if there was no service set for the given user
- */
- private boolean sendActivityAssistDataLocked(@UserIdInt int userId,
- @NonNull IBinder activityToken, @NonNull Bundle data) {
- final IntelligencePerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- return service.sendActivityAssistDataLocked(activityToken, data);
- }
- return false;
- }
-
private ActivityManagerInternal getAmInternal() {
synchronized (mLock) {
if (mAm == null) {
@@ -112,7 +100,7 @@
final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
@Override
- public void startSession(int userId, @NonNull IBinder activityToken,
+ public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
@NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
int flags, @NonNull IResultReceiver result) {
Preconditions.checkNotNull(activityToken);
@@ -134,7 +122,7 @@
}
@Override
- public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+ public void sendEvents(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
@NonNull List<ContentCaptureEvent> events) {
Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(events);
@@ -146,7 +134,7 @@
}
@Override
- public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+ public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId) {
Preconditions.checkNotNull(sessionId);
synchronized (mLock) {
@@ -168,14 +156,13 @@
private final class LocalService extends IntelligenceManagerInternal {
@Override
- public boolean isIntelligenceServiceForUser(int uid, int userId) {
+ public boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId) {
synchronized (mLock) {
final IntelligencePerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
return service.isIntelligenceServiceForUserLocked(uid);
}
}
-
return false;
}
@@ -183,8 +170,26 @@
public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
@NonNull Bundle data) {
synchronized (mLock) {
- return sendActivityAssistDataLocked(userId, activityToken, data);
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.sendActivityAssistDataLocked(activityToken, data);
+ }
}
+ return false;
+ }
+
+ @Override
+ public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+ @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ synchronized (mLock) {
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.requestAutofill(client, activityToken, autofillSessionId,
+ focusedId);
+ }
+ }
+ return null;
}
}
}
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 9694ab9..051f0d6 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -22,6 +22,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -36,12 +37,15 @@
import android.service.intelligence.SnapshotData;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
import android.view.intelligence.IntelligenceManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.server.AbstractPerUserSystemService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
import java.util.List;
@@ -62,7 +66,7 @@
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
protected IntelligencePerUserService(
- IntelligenceManagerService master, Object lock, int userId) {
+ IntelligenceManagerService master, Object lock, @UserIdInt int userId) {
super(master, lock, userId);
}
@@ -210,6 +214,17 @@
return uid == getServiceUidLocked();
}
+ @GuardedBy("mLock")
+ private ContentCaptureSession getSession(@NonNull IBinder activityToken) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ if (session.mActivityToken.equals(activityToken)) {
+ return session;
+ }
+ }
+ return null;
+ }
+
/**
* Destroys the service and all state associated with it.
*
@@ -226,6 +241,22 @@
mSessions.clear();
}
+ public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
+ @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
+ synchronized (mLock) {
+ final ContentCaptureSession session = getSession(activityToken);
+ if (session != null) {
+ // TODO(b/111330312): log metrics
+ if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()");
+ return session.requestAutofillLocked(client, autofillSessionId, focusedId);
+ }
+ if (mMaster.debug) {
+ Slog.d(TAG, "requestAutofill(): no session for " + activityToken);
+ }
+ return null;
+ }
+ }
+
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index a27c1cf..00c5b6a 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
@@ -28,8 +29,12 @@
import android.service.intelligence.SnapshotData;
import android.text.format.DateUtils;
import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
+import com.android.internal.os.IResultReceiver;
import com.android.server.AbstractRemoteService;
import java.util.List;
@@ -39,7 +44,7 @@
private static final String TAG = "RemoteIntelligenceService";
private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
private final RemoteIntelligenceServiceCallbacks mCallbacks;
private IIntelligenceService mService;
@@ -101,6 +106,25 @@
scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
}
+ /**
+ * Called by {@link ContentCaptureSession} to request augmented autofill.
+ */
+ public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId,
+ @NonNull IAutoFillManagerClient client, int autofillSessionId,
+ @NonNull AutofillId focusedId) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId,
+ focusedId));
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} when it's time to destroy all augmented autofill
+ * requests.
+ */
+ public void onDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+ }
private abstract static class MyPendingRequest
extends PendingRequest<RemoteIntelligenceService> {
@@ -124,8 +148,9 @@
final RemoteIntelligenceService remoteService = getService();
if (remoteService != null) {
try {
- myRun(remoteService);
// We don't expect the service to call us back, so we finish right away.
+ myRun(remoteService);
+ // TODO(b/111330312): not true anymore!!
finish();
} catch (RemoteException e) {
Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
@@ -191,6 +216,53 @@
}
}
+ private static final class PendingAutofillRequest extends MyPendingRequest {
+ private final @NonNull AutofillId mFocusedId;
+ private final @NonNull IAutoFillManagerClient mClient;
+ private final int mAutofillSessionId;
+
+ protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service,
+ @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client,
+ int autofillSessionId, @NonNull AutofillId focusedId) {
+ super(service, sessionId);
+ mClient = client;
+ mAutofillSessionId = autofillSessionId;
+ mFocusedId = focusedId;
+ }
+
+ @Override // from MyPendingRequest
+ public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ final IBinder realClient = resultData
+ .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
+ remoteService.mService.onAutofillRequest(mSessionId, realClient,
+ mAutofillSessionId, mFocusedId);
+ }
+ };
+
+ // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+ // cache IAugmentedAutofillManagerClient reference, etc...
+ mClient.getAugmentedAutofillClient(receiver);
+ }
+ }
+
+ private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
+
+ protected PendingDestroyAutofillWindowsRequest(@NonNull RemoteIntelligenceService service,
+ @NonNull InteractionSessionId sessionId) {
+ super(service, sessionId);
+ }
+
+ @Override
+ protected void myRun(@NonNull RemoteIntelligenceService service) throws RemoteException {
+ service.mService.onDestroyAutofillWindowsRequest(mSessionId);
+ // TODO(b/111330312): implement timeout
+ }
+ }
+
public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
// To keep it simple, we use the same callback for all failures / timeouts.
void onFailureOrTimeout(boolean timedOut);
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 3266b8b..fcd29e1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,10 +38,8 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -68,16 +66,15 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.Application;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
-import android.app.ITransientNotification;
-import android.app.IUriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
@@ -100,7 +97,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
@@ -116,7 +112,6 @@
import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.Log;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
@@ -288,6 +283,7 @@
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+ when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -1735,7 +1731,8 @@
}
@Test
- public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
+ throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
@@ -2509,6 +2506,7 @@
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+ verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
}
@Test
@@ -2517,8 +2515,11 @@
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
}
@@ -2529,8 +2530,12 @@
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(
+ eq(r.sbn), eq(false), eq((false)));
}
@Test
@@ -3459,11 +3464,12 @@
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+ assertTrue(mService.isCallerInstantApp(45770, 0));
info.privateFlags = 0;
- assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+ assertFalse(mService.isCallerInstantApp(575370, 0));
}
@Test
@@ -3472,8 +3478,9 @@
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+ assertTrue(mService.isCallerInstantApp(68638450, 10));
}
@Test
@@ -3689,4 +3696,19 @@
new TestableToastCallback(), 2000, 0);
assertEquals(1, mService.mToastQueue.size());
}
+
+ @Test
+ public void testOnNotificationSmartReplySent() {
+ final int replyIndex = 2;
+ final String reply = "Hello";
+ final boolean generatedByAssistant = true;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationSmartReplySent(
+ r.getKey(), replyIndex, reply, generatedByAssistant);
+ verify(mAssistants).notifyAssistantSuggestedReplySent(
+ eq(r.sbn), eq(reply), eq(generatedByAssistant));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index caabdbd..c2ab3ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -35,6 +35,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -66,6 +67,8 @@
import com.android.server.AttributeCache;
import com.android.server.ServiceThread;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentController;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
@@ -115,27 +118,13 @@
}
ActivityTaskManagerService createActivityTaskManagerService() {
- final TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(mContext));
- setupActivityManagerService(atm);
- return atm;
+ mService = new TestActivityTaskManagerService(mContext);
+ mSupervisor = mService.mStackSupervisor;
+ return mService;
}
void setupActivityTaskManagerService() {
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
- }
-
- ActivityManagerService createActivityManagerService() {
- final TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(mContext));
- return setupActivityManagerService(atm);
- }
-
- ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
- final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
- setupActivityManagerService(am, atm);
- return am;
+ createActivityTaskManagerService();
}
/** Creates a {@link TestActivityDisplay}. */
@@ -154,32 +143,6 @@
return display;
}
- void setupActivityManagerService(
- TestActivityManagerService am, TestActivityTaskManagerService atm) {
- atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
- atm.mAmInternal = am.getLocalService();
- am.mAtmInternal = atm.getLocalService();
- // Makes sure the supervisor is using with the spy object.
- atm.mStackSupervisor.setService(atm);
- doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
- doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
- PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
- doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
- doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
- doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- am.mActivityTaskManager = atm;
- am.mWindowManager = prepareMockWindowManager();
- atm.setWindowManager(am.mWindowManager);
-
- // Put a home stack on the default display, so that we'll always have something focusable.
- final TestActivityStackSupervisor supervisor =
- (TestActivityStackSupervisor) atm.mStackSupervisor;
- supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
- .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
- new ActivityBuilder(atm).setTask(task).build();
- }
-
/**
* Builder for creating new activities.
*/
@@ -405,23 +368,48 @@
}
}
- protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
- private LockTaskController mLockTaskController;
- private ActivityTaskManagerInternal mInternal;
+ protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
private PackageManagerInternal mPmInternal;
// ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
// We keep the reference in order to prevent creating it twice.
- private ActivityStackSupervisor mTestStackSupervisor;
+ ActivityStackSupervisor mTestStackSupervisor;
TestActivityTaskManagerService(Context context) {
super(context);
+ spyOn(this);
+
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
+
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
mSupportsSplitScreenMultiWindow = true;
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
- mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+ final TestActivityManagerService am =
+ new TestActivityManagerService(mTestInjector, this);
+
+ // Put a home stack on the default display, so that we'll always have something
+ // focusable.
+ final TestActivityStackSupervisor supervisor =
+ (TestActivityStackSupervisor) mStackSupervisor;
+ supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final TaskRecord task = new TaskBuilder(mStackSupervisor)
+ .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
+ new ActivityBuilder(this).setTask(task).build();
+
+ spyOn(getLifecycleManager());
+ spyOn(getLockTaskController());
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ }
+
+ void setActivityManagerService(IntentFirewall intentFirewall,
+ PendingIntentController intentController, ActivityManagerInternal amInternal,
+ WindowManagerService wm) {
+ mAmInternal = amInternal;
+ setActivityManagerService(intentFirewall, intentController);
+ setWindowManager(wm);
}
@Override
@@ -430,54 +418,17 @@
}
@Override
- public LockTaskController getLockTaskController() {
- if (mLockTaskController == null) {
- mLockTaskController = spy(super.getLockTaskController());
- }
-
- return mLockTaskController;
- }
-
- @Override
void updateUsageStats(ActivityRecord component, boolean resumed) {
}
@Override
- protected final ActivityStackSupervisor createStackSupervisor() {
+ protected ActivityStackSupervisor createStackSupervisor() {
if (mTestStackSupervisor == null) {
- final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
- final KeyguardController keyguardController = mock(KeyguardController.class);
-
- // Invoked during {@link ActivityStack} creation.
- doNothing().when(supervisor).updateUIDsPresentOnDisplay();
- // Always keep things awake.
- doReturn(true).when(supervisor).hasAwakeDisplay();
- // Called when moving activity to pinned stack.
- doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
- anyBoolean());
- // Do not schedule idle timeouts
- doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
- // unit test version does not handle launch wake lock
- doNothing().when(supervisor).acquireLaunchWakelock();
- doReturn(keyguardController).when(supervisor).getKeyguardController();
-
- supervisor.initialize();
- mTestStackSupervisor = supervisor;
+ mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
}
return mTestStackSupervisor;
}
- protected ActivityStackSupervisor createTestSupervisor() {
- return new TestActivityStackSupervisor(this, mH.getLooper());
- }
-
- ActivityTaskManagerInternal getLocalService() {
- if (mInternal == null) {
- mInternal = new ActivityTaskManagerService.LocalService();
- }
- return mInternal;
- }
-
@Override
PackageManagerInternal getPackageManagerInternalLocked() {
if (mPmInternal == null) {
@@ -524,24 +475,31 @@
}
}
+ // TODO: Replace this with a mock object since we are no longer in AMS package.
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
*/
- static class TestActivityManagerService extends ActivityManagerService {
+ class TestActivityManagerService extends ActivityManagerService {
- private ActivityManagerInternal mInternal;
-
- TestActivityManagerService(TestInjector testInjector) {
+ TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
super(testInjector, testInjector.mHandlerThread);
- mUgmInternal = mock(UriGrantsManagerInternal.class);
- }
+ spyOn(this);
- ActivityManagerInternal getLocalService() {
- if (mInternal == null) {
- mInternal = new LocalService();
- }
- return mInternal;
+ mWindowManager = prepareMockWindowManager();
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+ atm.setActivityManagerService(mIntentFirewall, mPendingIntentController,
+ new LocalService(), mWindowManager);
+
+ mActivityTaskManager = atm;
+ mAtmInternal = atm.mInternal;
+
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+ doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
+ doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+ doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
}
}
@@ -549,23 +507,40 @@
* An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
* setup not available in the test environment. Also specifies an injector for
*/
- protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
+ protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
private ActivityDisplay mDisplay;
private KeyguardController mKeyguardController;
- public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+ TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
super(service, looper);
+ spyOn(this);
mDisplayManager =
(DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = prepareMockWindowManager();
mKeyguardController = mock(KeyguardController.class);
setWindowContainerController(mock(RootWindowContainerController.class));
+
+ // Invoked during {@link ActivityStack} creation.
+ doNothing().when(this).updateUIDsPresentOnDisplay();
+ // Always keep things awake.
+ doReturn(true).when(this).hasAwakeDisplay();
+ // Called when moving activity to pinned stack.
+ doNothing().when(this).ensureActivitiesVisibleLocked(any(), anyInt(),
+ anyBoolean());
+ // Do not schedule idle timeouts
+ doNothing().when(this).scheduleIdleTimeoutLocked(any());
+ // unit test version does not handle launch wake lock
+ doNothing().when(this).acquireLaunchWakelock();
+ doReturn(mKeyguardController).when(this).getKeyguardController();
+
+ initialize();
}
@Override
public void initialize() {
super.initialize();
- mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
+ mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+ spyOn(mDisplay);
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 2f3f698..8596c77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -110,9 +110,7 @@
@Before
public void setUp() throws Exception {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mTestService = spy(new MyTestActivityTaskManagerService(mContext));
- final TestActivityManagerService am = spy(new MyTestActivityManagerService());
- setupActivityManagerService(am, mTestService);
+ mTestService = new MyTestActivityTaskManagerService(mContext);
mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
@@ -868,20 +866,11 @@
}
@Override
- protected ActivityStackSupervisor createTestSupervisor() {
- return new MyTestActivityStackSupervisor(this, mH.getLooper());
- }
-
- }
-
- private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService() {
- super(mTestInjector);
- }
-
- @Override
- public boolean isUserRunning(int userId, int flags) {
- return true;
+ protected ActivityStackSupervisor createStackSupervisor() {
+ if (mTestStackSupervisor == null) {
+ mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
+ }
+ return mTestStackSupervisor;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 50190e7..070f073 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -25,6 +25,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -50,61 +51,50 @@
public class RecentsAnimationTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private TestActivityTaskManagerService mTestService;
private ComponentName mRecentsComponent;
@Before
public void setUp() throws Exception {
mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
- mTestService = spy(new MyTestActivityTaskManagerService(mContext));
- setupActivityManagerService(mTestService);
+ mService = new TestActivityTaskManagerService(mContext);
+
+ final RecentTasks recentTasks = mService.getRecentTasks();
+ spyOn(recentTasks);
+ mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+ doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
}
@Test
public void testCancelAnimationOnStackOrderChange() {
ActivityStack fullscreenStack =
- mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
- ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+ ActivityRecord recentsActivity = new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
.setCreateTask(true)
.setStack(recentsStack)
.build();
ActivityStack fullscreenStack2 =
- mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+ ActivityRecord fsActivity = new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
.setCreateTask(true)
.setStack(fullscreenStack2)
.build();
- doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+ doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
// Start the recents animation
Intent recentsIntent = new Intent();
recentsIntent.setComponent(mRecentsComponent);
- mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+ mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
fullscreenStack.moveToFront("Activity start");
// Ensure that the recents animation was canceled
- verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
eq(REORDER_KEEP_IN_PLACE), any());
}
-
- private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
- MyTestActivityTaskManagerService(Context context) {
- super(context);
- }
-
- @Override
- protected RecentTasks createRecentTasks() {
- RecentTasks recents = mock(RecentTasks.class);
- doReturn(mRecentsComponent).when(recents).getRecentsComponent();
- System.out.println(mRecentsComponent);
- return recents;
- }
- }
}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 152831f..4f573a4 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,8 +27,6 @@
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -846,8 +844,6 @@
// Inform listeners if necessary
if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
|| event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -900,10 +896,6 @@
switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
- case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d940620..01e566c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,6 +20,7 @@
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.util.XmlUtils;
@@ -89,11 +90,23 @@
// Apply the offset to the beginTime to find the absolute time.
stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_ACTIVE_ATTR);
- stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
- parser, LAST_TIME_SERVICE_USED_ATTR);
+
+ try {
+ stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_SERVICE_USED_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+ }
+
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
- stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
+
+ try {
+ stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
TOTAL_TIME_SERVICE_USED_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+ }
+
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR,
0);
@@ -350,8 +363,17 @@
}
statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
- statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
- statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+ try {
+ statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse majorVersion", e);
+ }
+
+ try {
+ statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to parse minorVersion", e);
+ }
int eventCode;
int outerDepth = parser.getDepth();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5ad7c30..185c886 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1838,6 +1838,13 @@
"notify_international_call_on_wfc_bool";
/**
+ * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN,
+ * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN.
+ * Default value is false.
+ */
+ public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
+
+ /**
* Flag specifying whether to show an alert dialog for video call charges.
* By default this value is {@code false}.
* @hide
@@ -2643,6 +2650,7 @@
sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
false);
sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
null);
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index e373797..269cda1 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -37,6 +37,7 @@
import android.util.ArraySet;
import android.util.Log;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -107,14 +108,14 @@
* {@link MbmsGroupCallSession} that you received before calling this method again.
*
* @param context The {@link Context} to use.
- * @param executor The executor on which you wish to execute callbacks.
* @param subscriptionId The subscription ID to use.
+ * @param executor The executor on which you wish to execute callbacks.
* @param callback A callback object on which you wish to receive results of asynchronous
* operations.
* @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
*/
public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
- @NonNull Executor executor, int subscriptionId,
+ int subscriptionId, @NonNull Executor executor,
final @NonNull MbmsGroupCallSessionCallback callback) {
if (!sIsInitialized.compareAndSet(false, true)) {
throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
@@ -138,11 +139,11 @@
/**
* Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
- * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
*/
public static MbmsGroupCallSession create(@NonNull Context context,
@NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
- return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+ return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
}
/**
@@ -153,7 +154,7 @@
* instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
* enqueued will still be delivered.
*
- * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+ * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
* obtain another instance of {@link MbmsGroupCallSession} immediately after this method
* returns.
*
@@ -189,18 +190,19 @@
* Asynchronous errors through the callback include any of the errors in
* {@link MbmsErrors.GeneralErrors}.
*
- * @param executor The executor on which you wish to execute callbacks for this stream.
* @param tmgi The TMGI, an identifier for the group call you want to join.
- * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+ * @param saiList A list of SAIs for the group call that should be negotiated separately with
* the carrier.
- * @param frequencyArray An array of frequencies for the group call that should be negotiated
+ * @param frequencyList A lost of frequencies for the group call that should be negotiated
* separately with the carrier.
+ * @param executor The executor on which you wish to execute callbacks for this stream.
* @param callback The callback that you want to receive information about the call on.
* @return An instance of {@link GroupCall} through which the call can be controlled.
* May be {@code null} if an error occurred.
*/
- public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
- int[] frequencyArray, @NonNull GroupCallCallback callback) {
+ public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+ @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+ @NonNull GroupCallCallback callback) {
IMbmsGroupCallService groupCallService = mService.get();
if (groupCallService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -215,7 +217,7 @@
try {
int returnCode = groupCallService.startGroupCall(
- mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+ mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
if (returnCode == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index bdba8c8..2d46ec2 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,8 +17,8 @@
package android.telephony.emergency;
import android.annotation.IntDef;
-import android.hardware.radio.V1_3.EmergencyNumberSource;
-import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.hardware.radio.V1_4.EmergencyNumberSource;
+import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
import android.os.Parcelable;
@@ -138,7 +138,7 @@
}
/**
- * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+ * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
*
* The emergency number has one or more defined emergency number sources.
*
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
index 9aca18e..25e274e 100644
--- a/telephony/java/android/telephony/mbms/GroupCall.java
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.RemoteException;
import android.telephony.MbmsGroupCallSession;
import android.telephony.mbms.vendor.IMbmsGroupCallService;
@@ -24,6 +25,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* Class used to represent a single MBMS group call. After a call has been started with
@@ -41,8 +43,26 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
public @interface GroupCallState {}
+
+ /**
+ * Indicates that the group call is in a stopped state
+ *
+ * This can be reported after network action or after calling {@link #close}.
+ */
public static final int STATE_STOPPED = 1;
+
+ /**
+ * Indicates that the group call is started.
+ *
+ * Data can be transmitted and received in this state.
+ */
public static final int STATE_STARTED = 2;
+
+ /**
+ * Indicates that the group call is stalled.
+ *
+ * This may be due to a network issue or the device being temporarily out of range.
+ */
public static final int STATE_STALLED = 3;
/**
@@ -122,16 +142,17 @@
* Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
* information of the group call has * changed. Callers must obtain this information from the
* wireless carrier independently.
- * @param saiArray New array of SAIs that the call is available on.
- * @param frequencyArray New array of frequencies that the call is available on.
+ * @param saiList New list of SAIs that the call is available on.
+ * @param frequencyList New list of frequencies that the call is available on.
*/
- public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+ public void updateGroupCall(@NonNull List<Integer> saiList,
+ @NonNull List<Integer> frequencyList) {
if (mService == null) {
throw new IllegalStateException("No group call service attached");
}
try {
- mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+ mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 001bb02..77e36bb 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.Nullable;
import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
* A callback class for use when the application is in a group call. The middleware
* will provide updates on the status of the call via this callback.
*/
-public class GroupCallCallback {
+public interface GroupCallCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -40,7 +41,7 @@
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
- private @interface GroupCallError{}
+ @interface GroupCallError{}
/**
* Indicates broadcast signal strength is not available for this call.
@@ -48,7 +49,7 @@
* This may be due to the call no longer being available due to geography
* or timing (end of service)
*/
- public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+ int SIGNAL_STRENGTH_UNAVAILABLE = -1;
/**
* Called by the middleware when it has detected an error condition in this group call. The
@@ -56,9 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(@GroupCallError int errorCode, @Nullable String message) {
- // default implementation empty
- }
+ void onError(@GroupCallError int errorCode, @Nullable String message);
/**
* Called to indicate this call has changed state.
@@ -66,10 +65,8 @@
* See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
* and {@link GroupCall#STATE_STALLED}.
*/
- public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
- @GroupCall.GroupCallStateChangeReason int reason) {
- // default implementation empty
- }
+ void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+ @GroupCall.GroupCallStateChangeReason int reason);
/**
* Broadcast Signal Strength updated.
@@ -81,7 +78,5 @@
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this call due to timing, geography or popularity.
*/
- public void onBroadcastSignalStrengthUpdated(int signalStrength) {
- // default implementation empty
- }
+ void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
}
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 7c4321b..52e4d33 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -140,5 +140,21 @@
public static final int ERROR_UNKNOWN_FILE_INFO = 403;
}
+ /**
+ * Indicates the errors that are applicable only to the group call use-case.
+ */
+ public static class GroupCallErrors {
+ private GroupCallErrors() { }
+ /** Indicates that the middleware was unable to start the group call. */
+ public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+ /**
+ * Indicates that the app called
+ * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+ * same {@code tmgi}.
+ */
+ public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+ }
+
private MbmsErrors() {}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 7da734e..04e7ba1 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.telephony.MbmsGroupCallSession;
@@ -29,9 +30,9 @@
/**
* A callback class that is used to receive information from the middleware on MBMS group-call
* services. An instance of this object should be passed into
- * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
*/
-public class MbmsGroupCallSessionCallback {
+public interface MbmsGroupCallSessionCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -48,7 +49,7 @@
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
- private @interface GroupCallError{}
+ @interface GroupCallError{}
/**
* Called by the middleware when it has detected an error condition. The possible error codes
@@ -56,8 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(@GroupCallError int errorCode, @Nullable String message) {
- }
+ void onError(@GroupCallError int errorCode, @Nullable String message);
/**
* Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,21 +70,22 @@
* @param availableSais A list of lists of available SAIS in neighboring cells, where each list
* contains the available SAIs in an individual cell.
*/
- public void onAvailableSaisUpdated(List<Integer> currentSais,
- List<List<Integer>> availableSais) {
- }
+ void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+ @NonNull List<List<Integer>> availableSais);
/**
* Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
- * via this callback may be used to establish a data-link interface with the modem before the
- * middleware is ready.
- * Note that this method may be called before {@link #onMiddlewareReady()}.
+ * via this callback may be used to establish a data-link interface with the modem.
+ *
+ * In order to establish the data-link interface, the multicast IP and port must be obtained
+ * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+ * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+ * callback.
*
* @param interfaceName The interface name for the data link.
* @param index The index for the data link.
*/
- public void onServiceInterfaceAvailable(String interfaceName, int index) {
- }
+ void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
/**
* Called to indicate that the middleware has been initialized and is ready.
@@ -94,6 +95,5 @@
* delivered via {@link #onError(int, String)} with error code
* {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/
- public void onMiddlewareReady() {
- }
+ void onMiddlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 721256a..44cc24a 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,11 +29,11 @@
void stopGroupCall(int subId, long tmgi);
- void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
- in int[] frequencyArray);
+ void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
+ in List frequencyList);
- int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
- in int[] frequencyArray, IGroupCallCallback callback);
+ int startGroupCall(int subscriptionId, long tmgi, in List saiList,
+ in List frequencyList, IGroupCallCallback callback);
void dispose(int subId);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 3734ca7..e86a47d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -115,15 +115,16 @@
}
@Override
- public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
- int[] frequencyArray) {
+ public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+ List frequencyList) {
MbmsGroupCallServiceBase.this.updateGroupCall(
- subscriptionId, tmgi, saiArray, frequencyArray);
+ subscriptionId, tmgi, saiList, frequencyList);
}
@Override
- public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
- final int[] frequencyArray, final IGroupCallCallback callback)
+ public int startGroupCall(final int subscriptionId, final long tmgi,
+ final List saiList,
+ final List frequencyList, final IGroupCallCallback callback)
throws RemoteException {
if (callback == null) {
throw new NullPointerException("Callback must not be null");
@@ -132,7 +133,7 @@
final int uid = Binder.getCallingUid();
int result = MbmsGroupCallServiceBase.this.startGroupCall(
- subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+ subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
@Override
public void onError(final int errorCode, final String message) {
try {
@@ -209,13 +210,13 @@
*
* @param subscriptionId The subscription id to use.
* @param tmgi The TMGI, an identifier for the group call.
- * @param saiArray An array of SAIs for the group call.
- * @param frequencyArray An array of frequencies for the group call.
+ * @param saiList A list of SAIs for the group call.
+ * @param frequencyList A list of frequencies for the group call.
* @param callback The callback object on which the app wishes to receive updates.
* @return Any error in {@link MbmsErrors.GeneralErrors}
*/
- public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
- GroupCallCallback callback) {
+ public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+ List<Integer> frequencyList, GroupCallCallback callback) {
throw new UnsupportedOperationException("Not implemented");
}
@@ -237,11 +238,11 @@
/**
* Called when the app receives new SAI and frequency information for the group call identified
* by {@code tmgi}.
- * @param saiArray New array of SAIs that the call is available on.
- * @param frequencyArray New array of frequencies that the call is available on.
+ * @param saiList New list of SAIs that the call is available on.
+ * @param frequencyList New list of frequencies that the call is available on.
*/
- public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
- int[] frequencyArray) {
+ public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+ List<Integer> frequencyList) {
throw new UnsupportedOperationException("Not implemented");
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index af7123b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -27,9 +27,17 @@
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
@@ -40,6 +48,8 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -48,12 +58,19 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PermissionMonitorTest {
- private static final int MOCK_UID = 10001;
- private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+ private static final int MOCK_USER1 = 0;
+ private static final int MOCK_USER2 = 1;
+ private static final int MOCK_UID1 = 10001;
+ private static final String MOCK_PACKAGE1 = "appName1";
+ private static final String SYSTEM_PACKAGE1 = "sysName1";
+ private static final String SYSTEM_PACKAGE2 = "sysName2";
private static final String PARTITION_SYSTEM = "system";
private static final String PARTITION_OEM = "oem";
private static final String PARTITION_PRODUCT = "product";
@@ -63,6 +80,7 @@
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
+ @Mock private INetworkManagementService mNMS;
private PermissionMonitor mPermissionMonitor;
@@ -70,8 +88,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
- mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
}
private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -80,7 +97,8 @@
packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
packageInfo.applicationInfo.uid = uid;
when(mPackageManager.getPackageInfoAsUser(
- eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+ eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
}
@@ -143,16 +161,16 @@
@Test
public void testHasUseBackgroundNetworksPermission() throws Exception {
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
- assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
- assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+ assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
}
@Test
@@ -172,15 +190,150 @@
@Test
public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+ assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
- assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
- assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+ assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+ }
+
+ private class NMSMonitor {
+ private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+ NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+ // Add hook to verify and track result of setPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final Boolean isSystem = args[0].equals("SYSTEM");
+ for (final int uid : (int[]) args[1]) {
+ // TODO: Currently, permission monitor will send duplicate commands for each uid
+ // corresponding to each user. Need to fix that and uncomment below test.
+ // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+ // fail("uid " + uid + " is already set to " + isSystem);
+ // }
+ mApps.put(uid, isSystem);
+ }
+ return null;
+ }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+ // Add hook to verify and track result of clearPermission.
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ for (final int uid : (int[]) args[0]) {
+ // TODO: Currently, permission monitor will send duplicate commands for each uid
+ // corresponding to each user. Need to fix that and uncomment below test.
+ // if (!mApps.containsKey(uid)) {
+ // fail("uid " + uid + " does not exist.");
+ // }
+ mApps.remove(uid);
+ }
+ return null;
+ }).when(mockNMS).clearPermission(any(int[].class));
+ }
+
+ public void expectPermission(Boolean permission, int[] users, int[] apps) {
+ for (final int user : users) {
+ for (final int app : apps) {
+ final int uid = UserHandle.getUid(user, app);
+ if (!mApps.containsKey(uid)) {
+ fail("uid " + uid + " does not exist.");
+ }
+ if (mApps.get(uid) != permission) {
+ fail("uid " + uid + " has wrong permission: " + permission);
+ }
+ }
+ }
+ }
+
+ public void expectNoPermission(int[] users, int[] apps) {
+ for (final int user : users) {
+ for (final int app : apps) {
+ final int uid = UserHandle.getUid(user, app);
+ if (mApps.containsKey(uid)) {
+ fail("uid " + uid + " has listed permissions, expected none.");
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testUserAndPackageAddRemove() throws Exception {
+ final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+ // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+ // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+ // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(SYSTEM_PACKAGE1));
+ doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(SYSTEM_PACKAGE2));
+ doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+ eq(MOCK_PACKAGE1));
+
+ // Add SYSTEM_PACKAGE2, expect only have network permission.
+ mPermissionMonitor.onUserAdded(MOCK_USER1);
+ addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+ // Add SYSTEM_PACKAGE1, expect permission escalate.
+ addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+ mPermissionMonitor.onUserAdded(MOCK_USER2);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+ mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{MOCK_UID1});
+
+ // Remove MOCK_UID1, expect no permission left for all user.
+ mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+ // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+ removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ mPermissionMonitor.onUserRemoved(MOCK_USER1);
+ mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+ // Remove all packages, expect no permission left.
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+ removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID, MOCK_UID1});
+
+ // Remove last user, expect no redundant clearPermission is invoked.
+ mPermissionMonitor.onUserRemoved(MOCK_USER2);
+ mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID, MOCK_UID1});
+ }
+
+ // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+ // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+ // called multiple times with the uid corresponding to each user.
+ private void addPackageForUsers(int[] users, String packageName, int uid) {
+ for (final int user : users) {
+ mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+ }
+ }
+
+ private void removePackageForUsers(int[] users, int uid) {
+ for (final int user : users) {
+ mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index aa8d325..87706b9 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -60,14 +60,27 @@
*/
private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
/**
+ * Whether this is an OWE network or not.
+ */
+ private boolean mIsEnhancedOpen;
+ /**
* Pre-shared key for use with WPA-PSK networks.
*/
- private @Nullable String mPskPassphrase;
+ private @Nullable String mWpa2PskPassphrase;
+ /**
+ * Pre-shared key for use with WPA3-SAE networks.
+ */
+ private @Nullable String mWpa3SaePassphrase;
/**
* The enterprise configuration details specifying the EAP method,
- * certificates and other settings associated with the EAP.
+ * certificates and other settings associated with the WPA-EAP networks.
*/
- private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
+ /**
+ * The enterprise configuration details specifying the EAP method,
+ * certificates and other settings associated with the SuiteB networks.
+ */
+ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
/**
* This is a network that does not broadcast its SSID, so an
* SSID-specific probe request must be used for scans.
@@ -94,8 +107,11 @@
public WifiNetworkConfigBuilder() {
mSsidPatternMatcher = null;
mBssidPatternMatcher = null;
- mPskPassphrase = null;
- mEnterpriseConfig = null;
+ mIsEnhancedOpen = false;
+ mWpa2PskPassphrase = null;
+ mWpa3SaePassphrase = null;
+ mWpa2EnterpriseConfig = null;
+ mWpa3EnterpriseConfig = null;
mIsHiddenSSID = false;
mIsAppInteractionRequired = false;
mIsUserInteractionRequired = false;
@@ -188,36 +204,81 @@
}
/**
- * Set the ASCII PSK passphrase for this network. Needed for authenticating to
- * WPA_PSK networks.
+ * Specifies whether this represents an Enhanced Open (OWE) network.
*
- * @param pskPassphrase PSK passphrase of the network.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ */
+ public WifiNetworkConfigBuilder setIsEnhancedOpen() {
+ mIsEnhancedOpen = true;
+ return this;
+ }
+
+ /**
+ * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to
+ * WPA2-PSK networks.
+ *
+ * @param passphrase passphrase of the network.
* @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
* method.
* @throws IllegalArgumentException if the passphrase is not ASCII encodable.
*/
- public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
- checkNotNull(pskPassphrase);
+ public WifiNetworkConfigBuilder setWpa2Passphrase(@NonNull String passphrase) {
+ checkNotNull(passphrase);
final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
- if (!asciiEncoder.canEncode(pskPassphrase)) {
+ if (!asciiEncoder.canEncode(passphrase)) {
throw new IllegalArgumentException("passphrase not ASCII encodable");
}
- mPskPassphrase = pskPassphrase;
+ mWpa2PskPassphrase = passphrase;
+ return this;
+ }
+
+ /**
+ * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to
+ * WPA3-SAE networks.
+ *
+ * @param passphrase passphrase of the network.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+ */
+ public WifiNetworkConfigBuilder setWpa3Passphrase(@NonNull String passphrase) {
+ checkNotNull(passphrase);
+ final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+ if (!asciiEncoder.canEncode(passphrase)) {
+ throw new IllegalArgumentException("passphrase not ASCII encodable");
+ }
+ mWpa3SaePassphrase = passphrase;
return this;
}
/**
* Set the associated enterprise configuration for this network. Needed for authenticating to
- * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+ * WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description.
*
* @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
* @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
* method.
*/
- public WifiNetworkConfigBuilder setEnterpriseConfig(
+ public WifiNetworkConfigBuilder setWpa2EnterpriseConfig(
@NonNull WifiEnterpriseConfig enterpriseConfig) {
checkNotNull(enterpriseConfig);
- mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ return this;
+ }
+
+ /**
+ * Set the associated enterprise configuration for this network. Needed for authenticating to
+ * WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description.
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+ * method.
+ */
+ public WifiNetworkConfigBuilder setWpa3EnterpriseConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
return this;
}
@@ -324,16 +385,38 @@
configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
}
- private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
- if (!TextUtils.isEmpty(mPskPassphrase)) {
- // WPA_PSK network.
+ private void setSecurityParamsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+ if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
- } else if (mEnterpriseConfig != null) {
- // WPA_EAP network
+ // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+ configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
+ } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+ // PMF mandatory for SAE.
+ configuration.requirePMF = true;
+ // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+ configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
+ } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
- } else {
- // Open network
+ configuration.enterpriseConfig = mWpa2EnterpriseConfig;
+ } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+ // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
+ configuration.allowedGroupMgmtCiphers.set(
+ WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
+ configuration.allowedSuiteBCiphers.set(
+ WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+ configuration.allowedSuiteBCiphers.set(
+ WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+ configuration.requirePMF = true;
+ configuration.enterpriseConfig = mWpa3EnterpriseConfig;
+ } else if (mIsEnhancedOpen) { // OWE network
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+ // PMF mandatory.
+ configuration.requirePMF = true;
+ } else { // Open network
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
}
}
@@ -349,12 +432,7 @@
if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
}
- setKeyMgmtInWifiConfiguration(wifiConfiguration);
- // WifiConfiguration.preSharedKey needs quotes around ASCII password.
- if (mPskPassphrase != null) {
- wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
- }
- wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+ setSecurityParamsInWifiConfiguration(wifiConfiguration);
wifiConfiguration.hiddenSSID = mIsHiddenSSID;
wifiConfiguration.priority = mPriority;
wifiConfiguration.meteredOverride =
@@ -396,6 +474,20 @@
return false;
}
+ private void validateSecurityParams() {
+ int numSecurityTypes = 0;
+ numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
+ numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0;
+ numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
+ numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
+ numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
+ if (numSecurityTypes > 1) {
+ throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
+ + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig"
+ + " can be invoked for network specifier");
+ }
+ }
+
/**
* Create a specifier object used to request a Wi-Fi network. The generated
* {@link NetworkSpecifier} should be used in
@@ -464,10 +556,7 @@
+ "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
+ "specifier");
}
- if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
- throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
- + " be invoked for network specifier");
- }
+ validateSecurityParams();
return new WifiNetworkSpecifier(
mSsidPatternMatcher,
@@ -493,10 +582,7 @@
throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+ " allowed for suggestion");
}
- if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
- throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
- + "be invoked for suggestion");
- }
+ validateSecurityParams();
return new WifiNetworkSuggestion(
buildWifiConfiguration(),
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 8980ddb..c455c6f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
@@ -81,11 +82,11 @@
* pattern.
*/
@Test
- public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+ public void testWifiNetworkSpecifierBuilderForWpa2PskNetworkWithBssidPattern() {
NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
.setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK))
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.buildNetworkSpecifier();
assertTrue(specifier instanceof WifiNetworkSpecifier);
@@ -119,7 +120,7 @@
* SSID and BSSID pattern.
*/
@Test
- public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+ public void testWifiNetworkSpecifierBuilderForWpa2EapHiddenNetworkWithSsidAndBssid() {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
@@ -127,7 +128,7 @@
NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
.setBssid(MacAddress.fromString(TEST_BSSID))
- .setEnterpriseConfig(enterpriseConfig)
+ .setWpa2EnterpriseConfig(enterpriseConfig)
.setIsHiddenSsid()
.buildNetworkSpecifier();
@@ -174,14 +175,14 @@
}
/**
- * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+ * Ensure {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} throws an exception
* when the string is not ASCII encodable.
*/
@Test(expected = IllegalArgumentException.class)
- public void testSetPskPassphraseWithNonAsciiString() {
+ public void testSetWpa2PasphraseWithNonAsciiString() {
new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase("salvē")
+ .setWpa2Passphrase("salvē")
.buildNetworkSpecifier();
}
@@ -275,15 +276,15 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
- * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
- * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+ * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+ public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndEnterpriseConfig() {
new WifiNetworkConfigBuilder()
.setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
- .setPskPassphrase(TEST_PRESHARED_KEY)
- .setEnterpriseConfig(new WifiEnterpriseConfig())
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setWpa2EnterpriseConfig(new WifiEnterpriseConfig())
.buildNetworkSpecifier();
}
@@ -375,10 +376,11 @@
* app interaction and has a priority of zero set.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+ public void
+ testWifiNetworkSuggestionBuilderForWpa2EapNetworkWithPriorityAndReqAppInteraction() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsAppInteractionRequired()
.setPriority(0)
.buildNetworkSuggestion();
@@ -401,10 +403,11 @@
* user interaction and is metered.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+ public void
+ testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithMeteredAndReqUserInteraction() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
- .setPskPassphrase(TEST_PRESHARED_KEY)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsUserInteractionRequired()
.setIsMetered()
.buildNetworkSuggestion();
@@ -422,6 +425,74 @@
}
/**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setIsEnhancedOpen()
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.OWE));
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SAE network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3PskNetwork() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SAE));
+ assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+ suggestion.wifiConfiguration.preSharedKey);
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ }
+
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseConfig(enterpriseConfig)
+ .buildNetworkSuggestion();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+ .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
+ assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+ .get(WifiConfiguration.SuiteBCipher.ECDHE_RSA));
+ assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ }
+
+ /**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
* when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
*/
@@ -478,4 +549,46 @@
.setPriority(-1)
.buildNetworkSuggestion();
}
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndWpa3Passphrase() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkConfigBuilder#setIsEnhancedOpen(} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnhancedOpen() {
+ new WifiNetworkConfigBuilder()
+ .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setIsEnhancedOpen()
+ .buildNetworkSpecifier();
+ }
}