Merge "Make View#transformMatrixToLocal/Global public"
diff --git a/Android.bp b/Android.bp
index f40aab1..a80a5d3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -348,6 +348,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",
diff --git a/api/current.txt b/api/current.txt
index b177494..1bd955a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1319,6 +1319,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 +5230,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 +5462,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 +6355,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;
}
@@ -10129,6 +10133,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 +22644,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 +22656,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 +22693,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 +22701,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 +22720,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 +22951,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 +24591,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 +29134,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 +42891,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 +43239,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 +44246,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 +44258,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 +44301,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 +44318,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 {
@@ -49099,7 +49112,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);
diff --git a/api/system-current.txt b/api/system-current.txt
index 75a2cc4..f963c10 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4938,12 +4938,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 +5000,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,8 +5076,10 @@
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>);
@@ -7023,9 +7077,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 +7571,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..0fa83f1 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,8 +1156,10 @@
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 final void unsnoozeNotification(java.lang.String);
@@ -1318,9 +1321,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/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a770b9..ab1bf36 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
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 805fb68..41166dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -157,7 +157,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -5324,16 +5323,6 @@
}
}
- /**
- * Updates the application info.
- *
- * This only works in the system process. Must be called on the main thread.
- */
- public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
- Preconditions.checkState(mSystemThread, "Must only be called in the system process");
- handleApplicationInfoChanged(ai);
- }
-
void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
// Updates triggered by package installation go through a package update
// receiver. Here we try to capture ApplicationInfo changes that are
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/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/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 949cdd6..437039d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,7 +42,6 @@
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -3244,18 +3243,8 @@
Objects.requireNonNull(uri);
Objects.requireNonNull(size);
- // Older apps might be relying on mutable results, so only consider
- // giving them hardware bitmaps once they target Q or higher. If modern
- // apps need mutable thumbnails, they can always roll their own logic.
- final int allocator;
- if (getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- allocator = ImageDecoder.ALLOCATOR_DEFAULT;
- } else {
- allocator = ImageDecoder.ALLOCATOR_SOFTWARE;
- }
-
try (ContentProviderClient client = acquireContentProviderClient(uri)) {
- return loadThumbnail(client, uri, size, signal, allocator);
+ return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 75f5b17..6bfddb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3589,6 +3589,17 @@
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";
+
/**
* 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/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/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 16d454d..e032c18 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1232,7 +1232,7 @@
public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
Point size, CancellationSignal signal) throws IOException {
return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
- ImageDecoder.ALLOCATOR_DEFAULT);
+ ImageDecoder.ALLOCATOR_SOFTWARE);
}
/**
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..b92f8f1
--- /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.
+ *
+ * @throw {@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..0988510 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -47,4 +47,6 @@
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);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c1a3c2b..90f4792 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -160,6 +160,21 @@
}
/**
+ * 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) {}
+
+ /**
* 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 +270,33 @@
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();
+ }
}
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 MyHandler(Looper looper) {
super(looper, null, false);
@@ -305,6 +341,22 @@
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;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64eae0c..a4db451 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,17 @@
}
@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 onNotificationChannelModification(String pkgName, UserHandle user,
NotificationChannel channel,
@ChannelOrGroupModificationTypes int modificationType) {
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/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ec63cd9..7815864 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1009,7 +1009,7 @@
Log.e(TAG, "null synthesis text");
return false;
}
- if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) {
+ if (mText.length() > TextToSpeech.getMaxSpeechInputLength()) {
Log.w(TAG, "Text too long: " + mText.length() + " chars");
return false;
}
@@ -1609,7 +1609,7 @@
synchronized (mCallerToCallback) {
mCallerToCallback.remove(caller);
}
- //mSynthHandler.stopForApp(caller);
+ mSynthHandler.stopForApp(caller);
}
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fe7f220..cb454ff 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8152,6 +8152,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 +8168,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 */
@@ -8986,10 +8982,8 @@
if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
final ViewStructure structure = im.newViewStructure(this);
- boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
- if (notifyMgr) {
- im.notifyViewAppeared(structure);
- }
+ onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ im.notifyViewAppeared(structure);
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
} else {
if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
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/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..3e57e1d 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -58,7 +58,6 @@
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -523,10 +522,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 +533,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 +560,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) {
@@ -698,53 +736,67 @@
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;
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,17 @@
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()));
+ }
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1093719..4f1417e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2695,8 +2695,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..3cd0748 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
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/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3861739..826cd89 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -109,6 +109,13 @@
uid -> 1000 <= uid && uid < 2000;
/**
+ * Do not report any threads that have a total CPU usage (across all frequencies) less than or
+ * equal to this number. This significantly reduces the amount of reported threads without
+ * losing any important information
+ */
+ private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20;
+
+ /**
* Value returned when there was an error getting an integer ID value (e.g. PID, UID)
*/
private static final int ID_ERROR = -1;
@@ -339,6 +346,15 @@
}
int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
+ // Filter threads that have low total CPU usage
+ int cpuUsageSum = 0;
+ for (int i = 0; i < cpuUsages.length; i++) {
+ cpuUsageSum += cpuUsages[i];
+ }
+ if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) {
+ return null;
+ }
+
return new ThreadCpuUsage(threadId, threadName, cpuUsages);
}
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..9204ee4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2989,6 +2989,12 @@
<!-- 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>
+
<!-- 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. -->
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..7a78327 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,8 @@
<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="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
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/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/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/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b242a34..385bad5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,7 +39,9 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -56,8 +58,8 @@
1000, 2000, 3000, 4000,
};
private static final int[][] THREAD_CPU_TIMES = {
- {1, 0, 0, 1},
- {0, 0, 0, 0},
+ {100, 0, 0, 100},
+ {0, 0, 9999999, 0},
{1000, 1000, 1000, 1000},
{0, 1, 2, 3},
};
@@ -108,6 +110,42 @@
}
@Test
+ public void testReader_filtersLowTotalCpuUsage() throws IOException {
+ KernelCpuThreadReader.Injector processUtils =
+ new KernelCpuThreadReader.Injector() {
+ @Override
+ public int myPid() {
+ return PROCESS_ID;
+ }
+
+ @Override
+ public int myUid() {
+ return UID;
+ }
+
+ @Override
+ public int getUidForPid(int pid) {
+ return 0;
+ }
+ };
+ setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME,
+ THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}});
+
+ final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+ mProcDirectory.toPath(),
+ mProcDirectory.toPath().resolve("self/task/1/time_in_state"),
+ processUtils);
+ final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ kernelCpuThreadReader.getCurrentProcessCpuUsage();
+
+ List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream()
+ .map(t -> t.threadId)
+ .collect(Collectors.toList());
+ assertEquals(1, threadIds.size());
+ assertEquals(2, (long) threadIds.get(0));
+ }
+
+ @Test
public void testReader_byUids() throws IOException {
int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -134,7 +172,7 @@
setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
new int[]{uid * 10},
"process" + uid, new String[]{"thread" + uid}, new int[]{1000},
- new int[][]{{uid}});
+ new int[][]{{uid + 100}});
}
final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
mProcDirectory.toPath(),
@@ -151,7 +189,7 @@
int uid = expectedUids[i];
checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
- new int[]{1000}, new int[][]{{uid}});
+ new int[]{1000}, new int[][]{{uid + 100}});
}
}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index fdd638a..dea2f45 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -108,11 +108,10 @@
* Scaled mask based on the view bounds.
*/
private final Path mMask;
+ private final Path mMaskScaleOnly;
private final Matrix mMaskMatrix;
private final Region mTransparentRegion;
- private Bitmap mMaskBitmap;
-
/**
* Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
* background layer.
@@ -156,8 +155,8 @@
sMask = PathParser.createPathFromPathData(
Resources.getSystem().getString(R.string.config_icon_mask));
}
- mMask = PathParser.createPathFromPathData(
- Resources.getSystem().getString(R.string.config_icon_mask));
+ mMask = new Path(sMask);
+ mMaskScaleOnly = new Path(mMask);
mMaskMatrix = new Matrix();
mCanvas = new Canvas();
mTransparentRegion = new Region();
@@ -329,24 +328,19 @@
}
private void updateMaskBoundsInternal(Rect b) {
+ // reset everything that depends on the view bounds
mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+ sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+ mMaskMatrix.postTranslate(b.left, b.top);
sMask.transform(mMaskMatrix, mMask);
- if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() ||
- mMaskBitmap.getHeight() != b.height()) {
- mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+ if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+ || mLayersBitmap.getHeight() != b.height()) {
mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
}
- // mMaskBitmap bound [0, w] x [0, h]
- mCanvas.setBitmap(mMaskBitmap);
- mPaint.setShader(null);
- mCanvas.drawPath(mMask, mPaint);
- // mMask bound [left, top, right, bottom]
- mMaskMatrix.postTranslate(b.left, b.top);
- mMask.reset();
- sMask.transform(mMaskMatrix, mMask);
- // reset everything that depends on the view bounds
+ mPaint.setShader(null);
mTransparentRegion.setEmpty();
mLayersShader = null;
}
@@ -371,9 +365,11 @@
mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint.setShader(mLayersShader);
}
- if (mMaskBitmap != null) {
+ if (mMaskScaleOnly != null) {
Rect bounds = getBounds();
- canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint);
+ canvas.translate(bounds.left, bounds.top);
+ canvas.drawPath(mMaskScaleOnly, mPaint);
+ canvas.translate(-bounds.left, -bounds.top);
}
}
@@ -549,7 +545,7 @@
final ChildDrawable[] layers = mLayerState.mChildren;
for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
- if (layers[i].mDrawable.isProjected()) {
+ if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) {
return true;
}
}
@@ -674,7 +670,7 @@
@Override
public int getAlpha() {
- return PixelFormat.TRANSLUCENT;
+ return mPaint.getAlpha();
}
@Override
@@ -718,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/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1cb0d25..365be10 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -349,7 +349,7 @@
goto exit;
}
}
- ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+ ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc,
targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 76db18d..f7fb89e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -26,7 +26,9 @@
#include <algorithm>
#include <limits>
+#include <map>
#include <memory>
+#include <set>
#include <type_traits>
#include <android-base/macros.h>
@@ -7033,170 +7035,178 @@
return NO_ERROR;
}
-struct IdmapTypeMap {
- ssize_t overlayTypeId;
- size_t entryOffset;
- Vector<uint32_t> entryMap;
+struct IdmapMatchingResources {
+ void Add(uint32_t targetResId, uint32_t overlayResId) {
+ uint8_t targetTypeid = Res_GETTYPE(targetResId);
+ if (typeMappings.find(targetTypeid) == typeMappings.end()) {
+ typeMappings.emplace(targetTypeid, std::set<std::pair<uint32_t, uint32_t>>());
+ }
+ auto& entries = typeMappings[targetTypeid];
+ entries.insert(std::make_pair(targetResId, overlayResId));
+ }
+
+ void FixPadding() {
+ for (auto ti = typeMappings.cbegin(); ti != typeMappings.cend(); ++ti) {
+ uint32_t last_seen = 0xffffffff;
+ size_t total_entries = 0;
+ for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
+ assert(last_seen == 0xffffffff || last_seen < ei->first);
+ entryPadding[ei->first] = (last_seen == 0xffffffff) ? 0 : ei->first - last_seen - 1;
+ last_seen = ei->first;
+ total_entries += 1 + entryPadding[ei->first];
+ }
+ numberOfEntriesIncludingPadding[ti->first] = total_entries;
+ }
+ }
+
+ // resource type ID in context of target -> set of resource entries mapping target -> overlay
+ std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> typeMappings;
+
+ // resource ID in context of target -> trailing padding for that resource (call FixPadding
+ // before use)
+ std::map<uint32_t, size_t> entryPadding;
+
+ // resource type ID in context of target -> total number of entries, including padding entries,
+ // for that type (call FixPadding before use)
+ std::map<uint8_t, size_t> numberOfEntriesIncludingPadding;
};
-status_t ResTable::createIdmap(const ResTable& overlay,
+status_t ResTable::createIdmap(const ResTable& targetResTable,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
void** outData, size_t* outSize) const
{
- // see README for details on the format of map
- if (mPackageGroups.size() == 0) {
- ALOGW("idmap: target package has no package groups, cannot create idmap\n");
+ if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
+ ALOGE("idmap: unexpected NULL parameter");
+ return UNKNOWN_ERROR;
+ }
+ if (strlen(targetPath) > 255) {
+ ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
+ return UNKNOWN_ERROR;
+ }
+ if (strlen(overlayPath) > 255) {
+ ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
+ return UNKNOWN_ERROR;
+ }
+ if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
+ ALOGE("idmap: invalid overlay package");
+ return UNKNOWN_ERROR;
+ }
+ if (targetResTable.mPackageGroups.size() == 0 ||
+ targetResTable.mPackageGroups[0]->packages.size() == 0) {
+ ALOGE("idmap: invalid target package");
return UNKNOWN_ERROR;
}
- if (mPackageGroups[0]->packages.size() == 0) {
- ALOGW("idmap: target package has no packages in its first package group, "
- "cannot create idmap\n");
- return UNKNOWN_ERROR;
- }
+ const ResTable_package* targetPackageStruct = targetResTable.mPackageGroups[0]->packages[0]->package;
+ const size_t tmpNameSize = arraysize(targetPackageStruct->name);
+ char16_t tmpName[tmpNameSize];
+ strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
+ const String16 targetPackageName(tmpName);
- // The number of resources overlaid that were not explicitly marked overlayable.
+ const PackageGroup* packageGroup = mPackageGroups[0];
+
+ // the number of resources overlaid that were not explicitly marked overlayable
size_t forcedOverlayCount = 0u;
- KeyedVector<uint8_t, IdmapTypeMap> map;
-
- // overlaid packages are assumed to contain only one package group
- const PackageGroup* pg = mPackageGroups[0];
-
- // starting size is header
- *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
-
- // target package id and number of types in map
- *outSize += 2 * sizeof(uint16_t);
-
- // overlay packages are assumed to contain only one package group
- const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
- char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
- strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
- const String16 overlayPackage(tmpName);
-
- for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
- const TypeList& typeList = pg->types[typeIndex];
+ // find the resources that exist in both packages
+ IdmapMatchingResources matchingResources;
+ for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
+ const TypeList& typeList = packageGroup->types[typeIndex];
if (typeList.isEmpty()) {
continue;
}
-
const Type* typeConfigs = typeList[0];
- IdmapTypeMap typeMap;
- typeMap.overlayTypeId = -1;
- typeMap.entryOffset = 0;
-
for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
- uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
- resource_name resName;
- if (!this->getResourceName(resID, false, &resName)) {
- if (typeMap.entryMap.isEmpty()) {
- typeMap.entryOffset++;
- }
+ uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
+ resource_name current_res;
+ if (!getResourceName(overlay_resid, false, ¤t_res)) {
continue;
}
uint32_t typeSpecFlags = 0u;
- const String16 overlayType(resName.type, resName.typeLen);
- const String16 overlayName(resName.name, resName.nameLen);
- uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
- overlayName.size(),
- overlayType.string(),
- overlayType.size(),
- overlayPackage.string(),
- overlayPackage.size(),
- &typeSpecFlags);
- if (overlayResID == 0) {
- // No such target resource was found.
- if (typeMap.entryMap.isEmpty()) {
- typeMap.entryOffset++;
- }
+ const uint32_t target_resid = targetResTable.identifierForName(
+ current_res.name,
+ current_res.nameLen,
+ current_res.type,
+ current_res.typeLen,
+ targetPackageName.string(),
+ targetPackageName.size(),
+ &typeSpecFlags);
+
+ if (target_resid == 0) {
continue;
}
- // Now that we know this is being overlaid, check if it can be, and emit a warning if
- // it can't.
if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
- forcedOverlayCount++;
+ ++forcedOverlayCount;
}
- if (typeMap.overlayTypeId == -1) {
- typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
- }
-
- if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
- ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
- " but entries should map to resources of type %02zx",
- resID, overlayResID, typeMap.overlayTypeId);
- return BAD_TYPE;
- }
-
- if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
- // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
- size_t index = typeMap.entryMap.size();
- size_t numItems = entryIndex - (typeMap.entryOffset + index);
- if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
- return NO_MEMORY;
- }
- }
- typeMap.entryMap.add(Res_GETENTRY(overlayResID));
- }
-
- if (!typeMap.entryMap.isEmpty()) {
- if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
- return NO_MEMORY;
- }
- *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
+ matchingResources.Add(target_resid, overlay_resid);
}
}
- if (map.isEmpty()) {
- ALOGW("idmap: no resources in overlay package present in base package");
+ if (matchingResources.typeMappings.empty()) {
+ ALOGE("idmap: no matching resources");
return UNKNOWN_ERROR;
}
+ matchingResources.FixPadding();
+
+ // write idmap
+ *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
+ *outSize += 2 * sizeof(uint16_t); // target package id, type count
+ const auto typesEnd = matchingResources.typeMappings.cend();
+ for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
+ *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
+ *outSize += matchingResources.numberOfEntriesIncludingPadding[ti->first] *
+ sizeof(uint32_t); // entries
+ }
if ((*outData = malloc(*outSize)) == NULL) {
return NO_MEMORY;
}
- uint32_t* data = (uint32_t*)*outData;
- *data++ = htodl(IDMAP_MAGIC);
- *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
- *data++ = htodl(targetCrc);
- *data++ = htodl(overlayCrc);
- const char* paths[] = { targetPath, overlayPath };
- for (int j = 0; j < 2; ++j) {
- char* p = (char*)data;
- const char* path = paths[j];
- const size_t I = strlen(path);
- if (I > 255) {
- ALOGV("path exceeds expected 255 characters: %s\n", path);
- return UNKNOWN_ERROR;
- }
- for (size_t i = 0; i < 256; ++i) {
- *p++ = i < I ? path[i] : '\0';
- }
- data += 256 / sizeof(uint32_t);
- }
- const size_t mapSize = map.size();
- uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
- *typeData++ = htods(pg->id);
- *typeData++ = htods(mapSize);
- for (size_t i = 0; i < mapSize; ++i) {
- uint8_t targetTypeId = map.keyAt(i);
- const IdmapTypeMap& typeMap = map[i];
- *typeData++ = htods(targetTypeId + 1);
- *typeData++ = htods(typeMap.overlayTypeId);
- *typeData++ = htods(typeMap.entryMap.size());
- *typeData++ = htods(typeMap.entryOffset);
+ // write idmap header
+ uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
+ *data++ = htodl(IDMAP_MAGIC); // write: magic
+ *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
+ *data++ = htodl(targetCrc); // write: target crc
+ *data++ = htodl(overlayCrc); // write: overlay crc
- const size_t entryCount = typeMap.entryMap.size();
- uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
- for (size_t j = 0; j < entryCount; j++) {
- entries[j] = htodl(typeMap.entryMap[j]);
+ char* charData = reinterpret_cast<char*>(data);
+ size_t pathLen = strlen(targetPath);
+ for (size_t i = 0; i < 256; ++i) {
+ *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
+ }
+ pathLen = strlen(overlayPath);
+ for (size_t i = 0; i < 256; ++i) {
+ *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
+ }
+ data += (2 * 256) / sizeof(uint32_t);
+
+ // write idmap data header
+ uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+ *typeData++ = htods(targetPackageStruct->id); // write: target package id
+ *typeData++ =
+ htods(static_cast<uint16_t>(matchingResources.typeMappings.size())); // write: type count
+
+ // write idmap data
+ for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
+ const size_t entryCount = matchingResources.numberOfEntriesIncludingPadding[ti->first];
+ auto ei = ti->second.cbegin();
+ *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
+ *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
+ *typeData++ = htods(entryCount); // write: entry count
+ *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
+ uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
+ for (; ei != ti->second.cend(); ++ei) {
+ const size_t padding = matchingResources.entryPadding[ei->first];
+ for (size_t i = 0; i < padding; ++i) {
+ *entryData++ = htodl(0xffffffff); // write: padding
+ }
+ *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
}
typeData += entryCount * 2;
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 59abad4..ad33fcf 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1990,7 +1990,7 @@
// Return value: on success: NO_ERROR; caller is responsible for free-ing
// outData (using free(3)). On failure, any status_t value other than
// NO_ERROR; the caller should not free outData.
- status_t createIdmap(const ResTable& overlay,
+ status_t createIdmap(const ResTable& targetResTable,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
void** outData, size_t* outSize) const;
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 26d2896..10b83a7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -40,7 +40,7 @@
ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
char target_name[256] = "com.android.basic";
- ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name,
+ ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
&data_, &data_size_));
}
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/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 60153fc..0e4900a 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -355,6 +355,21 @@
}
@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 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/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 4a0bc9b..31dd46c 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] -->
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 0818513..450d34d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -108,13 +108,13 @@
VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final boolean ENABLE_HEADS_UP = true;
- protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
- protected final NotificationMessagingUtil mMessagingUtil;
+ private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
- protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+ private final NotificationClicker mNotificationClicker = new NotificationClicker();
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
@@ -145,14 +145,15 @@
private NotificationPresenter mPresenter;
private Callback mCallback;
protected PowerManager mPowerManager;
- protected NotificationListenerService.RankingMap mLatestRankingMap;
+ private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- protected ContentObserver mHeadsUpObserver;
+ private ContentObserver mHeadsUpObserver;
protected boolean mUseHeadsUp = false;
- protected boolean mDisableNotificationAlerts;
+ private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
- protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
+ @VisibleForTesting
+ final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@@ -485,22 +486,6 @@
updateNotifications();
}
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(StatusBarNotification n, String message) {
- removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
- try {
- mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
- n.getInitialPid(), message, n.getUserId());
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
private void abortExistingInflation(String key) {
if (mPendingNotifications.containsKey(key)) {
NotificationData.Entry entry = mPendingNotifications.get(key);
@@ -513,13 +498,31 @@
}
}
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
@Override
- public void handleInflationException(StatusBarNotification notification, Exception e) {
- handleNotificationError(notification, e.getMessage());
+ public void handleInflationException(StatusBarNotification n, Exception e) {
+ removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
+ try {
+ mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+ n.getInitialPid(), e.getMessage(), n.getUserId());
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
}
private void addEntry(NotificationData.Entry shadeEntry) {
- addNotificationViews(shadeEntry);
+ if (shadeEntry == null) {
+ return;
+ }
+ // Add the expanded view and icon.
+ mNotificationData.add(shadeEntry);
+ tagForeground(shadeEntry.notification);
+ updateNotifications();
mCallback.onNotificationAdded(shadeEntry);
}
@@ -755,17 +758,7 @@
row.inflateViews();
}
- protected void addNotificationViews(NotificationData.Entry entry) {
- if (entry == null) {
- return;
- }
- // Add the expanded view and icon.
- mNotificationData.add(entry);
- tagForeground(entry.notification);
- updateNotifications();
- }
-
- protected NotificationData.Entry createNotificationViews(
+ private NotificationData.Entry createNotificationViews(
StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
throws InflationException {
if (DEBUG) {
@@ -841,7 +834,7 @@
}
@VisibleForTesting
- protected void tagForeground(StatusBarNotification notification) {
+ void tagForeground(StatusBarNotification notification) {
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
notification.getUserId(), notification.getPackageName());
if (activeOps != null) {
@@ -1098,7 +1091,7 @@
* @param entry the entry to check
* @return true if the entry should ambient pulse, false otherwise
*/
- protected boolean shouldPulse(NotificationData.Entry entry) {
+ private boolean shouldPulse(NotificationData.Entry entry) {
StatusBarNotification sbn = entry.notification;
if (!getShadeController().isDozing()) {
@@ -1173,7 +1166,7 @@
return true;
}
- protected void setNotificationShown(StatusBarNotification n) {
+ private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1185,7 +1178,7 @@
}
}
- protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
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..daec9c9 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
@@ -3145,8 +3145,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..88edc0d 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;
/**
@@ -1322,6 +1325,10 @@
List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+ List<Notification.Action> sysGeneratedSmartActions =
+ notification.getAllowSystemGeneratedContextualActions()
+ ? entry.systemGeneratedSmartActions : Collections.emptyList();
+
if (appGeneratedSmartRepliesExist) {
return new SmartRepliesAndActions(remoteInputActionPair.first,
remoteInputActionPair.second.actionIntent,
@@ -1338,11 +1345,11 @@
return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
freeformRemoteInputActionPair.second.actionIntent,
entry.smartReplies,
- entry.systemGeneratedSmartActions,
+ sysGeneratedSmartActions,
freeformRemoteInputActionPair);
}
// App didn't generate anything, and there are no NAS-generated smart replies.
- return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
+ return new SmartRepliesAndActions(null, null, null, sysGeneratedSmartActions,
freeformRemoteInputActionPair);
}
@@ -1928,4 +1935,23 @@
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();
+ }
}
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/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/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..5e137a7 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
@@ -106,7 +106,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);
@@ -299,6 +300,23 @@
assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
}
+ @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.smartReplies, equalTo(null));
+ assertThat(repliesAndActions.smartActions, is(empty()));
+ }
+
private Notification.Action.Builder createActionBuilder(String actionTitle) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
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/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/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/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e70460a..d04fa23 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -247,9 +247,6 @@
// device).
private Point mStableDisplaySize = new Point();
- // Whether the system has finished booting or not.
- private boolean mSystemReady;
-
// The top inset of the default display.
// This gets persisted so that the boot animation knows how to transition from the display's
// full size to the size configured by the user. Right now we only persist and animate the top
@@ -322,8 +319,6 @@
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
mCurrentUserId = UserHandle.USER_SYSTEM;
-
- mSystemReady = false;
}
public void setupSchedulerPolicies() {
@@ -413,10 +408,6 @@
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
- mSystemReady = true;
- // Just in case the top inset changed before the system was ready. At this point, any
- // relevant configuration should be in place.
- recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
}
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -1065,10 +1056,7 @@
}
private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
- // We must only persist the inset after boot has completed, otherwise we will end up
- // overwriting the persisted value before the masking flag has been loaded from the
- // resource overlay.
- if (!mSystemReady || d == null) {
+ if (d == null) {
return;
}
int topInset = d.getInsets().top;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6f726e6..9566598 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -57,8 +57,6 @@
* </p>
*/
final class LogicalDisplay {
- private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";
-
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da29e4..2048d5f 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);
}
}
}
@@ -4398,19 +4401,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 +4733,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 +6525,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 +6850,104 @@
}
}
- 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);
+ }
+ });
+ }
+
+
/**
* 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/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1b03d1..d471904 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -22,14 +22,11 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityThread;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,7 +34,6 @@
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
@@ -59,11 +55,9 @@
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.util.ConcurrentUtils;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerService;
@@ -84,8 +78,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -228,8 +220,6 @@
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
- private Future<?> mInitCompleteSignal;
-
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
@@ -241,29 +231,28 @@
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
getDefaultOverlayPackages(), new OverlayChangeListener());
- mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, null);
- final IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(ACTION_USER_ADDED);
- userFilter.addAction(ACTION_USER_REMOVED);
- getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
- userFilter, null, null);
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+ packageFilter, null, null);
- restoreSettings();
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(ACTION_USER_ADDED);
+ userFilter.addAction(ACTION_USER_REMOVED);
+ getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
+ userFilter, null, null);
- initIfNeeded();
- onSwitchUser(UserHandle.USER_SYSTEM);
+ restoreSettings();
- publishBinderService(Context.OVERLAY_SERVICE, mService);
- publishLocalService(OverlayManagerService.class, this);
- }, "Init OverlayManagerService");
+ initIfNeeded();
+ onSwitchUser(UserHandle.USER_SYSTEM);
+
+ publishBinderService(Context.OVERLAY_SERVICE, mService);
+ publishLocalService(OverlayManagerService.class, this);
}
@Override
@@ -271,32 +260,6 @@
// Intentionally left empty.
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
- "Wait for OverlayManagerService init");
- mInitCompleteSignal = null;
- }
- }
-
- public void updateSystemUiContext() {
- if (mInitCompleteSignal != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
- "Wait for OverlayManagerService init");
- mInitCompleteSignal = null;
- }
-
- final ApplicationInfo ai;
- try {
- ai = mPackageManager.mPackageManager.getApplicationInfo("android",
- GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
- }
-
private void initIfNeeded() {
final UserManager um = getContext().getSystemService(UserManager.class);
final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
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..14b2f01c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1039,11 +1039,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 +2426,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 +2446,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 +2564,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/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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8ac41f..56f7cff 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.DisplayManagerInternal;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -717,17 +716,9 @@
// Manages Overlay packages
traceBeginAndSlog("StartOverlayManagerService");
- OverlayManagerService overlayManagerService = new OverlayManagerService(
- mSystemContext, installer);
- mSystemServiceManager.startService(overlayManagerService);
+ mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
traceEnd();
- if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
- // DisplayManager needs the overlay immediately.
- overlayManagerService.updateSystemUiContext();
- LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
- }
-
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
new file mode 100644
index 0000000..ebc816d
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+android_test {
+ name: "FrameworksMockingServicesTests",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "services.core",
+ "services.net",
+ "androidx.test.runner",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
deleted file mode 100644
index b21b3e4..0000000
--- a/services/tests/mockingservicestests/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- services.core \
- services.net \
- androidx.test.runner \
- mockito-target-extended-minus-junit4 \
- platform-test-annotations \
-
-LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
-
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
new file mode 100644
index 0000000..e804342
--- /dev/null
+++ b/services/tests/servicestests/Android.bp
@@ -0,0 +1,104 @@
+//########################################################################
+// Build FrameworksServicesTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksServicesTests",
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+
+ "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl",
+ "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl",
+
+ "test-apps/JobTestApp/src/**/*.java",
+
+ "test-apps/SuspendTestApp/src/**/*.java",
+ ],
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.accessibility",
+ "services.appwidget",
+ "services.autofill",
+ "services.backup",
+ "services.core",
+ "services.devicepolicy",
+ "services.net",
+ "services.usage",
+ "guava",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "ShortcutManagerTestUtils",
+ "truth-prebuilt",
+ "testables",
+ "testng",
+ "ub-uiautomator",
+ "platformprotosnano",
+ "hamcrest-library",
+ "servicestests-utils",
+ ],
+
+ aidl: {
+ local_include_dirs: ["aidl"],
+ },
+
+ libs: [
+ "android.hidl.manager-V1.0-java",
+ "android.hardware.tv.cec-V1.0-java",
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdaidl",
+ "libui",
+ "libunwind",
+ "libutils",
+ ],
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+}
+
+java_library {
+ name: "servicestests-utils",
+ srcs: [
+ "utils/**/*.java",
+ ],
+ static_libs: [
+ "android-support-test",
+ "mockito-target-minus-junit4",
+ ],
+ libs: [
+ "android.test.runner",
+ ],
+}
+
+filegroup {
+ name: "servicestests-SuspendTestApp-files",
+ srcs: [
+ "src/com/android/server/pm/SuspendPackagesTest.java",
+ ],
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
deleted file mode 100644
index e2f8995..0000000
--- a/services/tests/servicestests/Android.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-#########################################################################
-# Build FrameworksServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- services.accessibility \
- services.appwidget \
- services.autofill \
- services.backup \
- services.core \
- services.devicepolicy \
- services.net \
- services.usage \
- guava \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-minus-junit4 \
- platform-test-annotations \
- ShortcutManagerTestUtils \
- truth-prebuilt \
- testables \
- testng \
- ub-uiautomator\
- platformprotosnano \
- hamcrest-library
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
- aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
-
-LOCAL_JAVA_LIBRARIES := \
- android.hidl.manager-V1.0-java \
- android.hardware.tv.cec-V1.0-java \
- android.test.mock \
- android.test.base android.test.runner \
-
-LOCAL_PACKAGE_NAME := FrameworksServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcutils \
- liblog \
- liblzma \
- libnativehelper \
- libnetdaidl \
- libui \
- libunwind \
- libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
new file mode 100644
index 0000000..d4e53dd
--- /dev/null
+++ b/services/tests/servicestests/aidl/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 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.
+
+java_library {
+ name: "servicestests-aidl",
+ sdk_version: "current",
+ srcs: [
+ "com/android/servicestests/aidl/INetworkStateObserver.aidl",
+ "com/android/servicestests/aidl/ICmdReceiverService.aidl",
+ ],
+}
diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk
deleted file mode 100644
index 166da1d..0000000
--- a/services/tests/servicestests/aidl/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2017 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
- com/android/servicestests/aidl/INetworkStateObserver.aidl \
- com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_MODULE := servicestests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/services/tests/servicestests/test-apps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
new file mode 100644
index 0000000..13e6644
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "ConnTestApp",
+
+ test_suites: ["device-tests"],
+
+ static_libs: ["servicestests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
deleted file mode 100644
index 18b8c2d..0000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2017 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := ConnTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
new file mode 100644
index 0000000..ae1eca7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "JobTestApp",
+
+ sdk_version: "current",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
deleted file mode 100644
index 7893c91..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (C) 2017 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := JobTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
new file mode 100644
index 0000000..7257275
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+android_test_helper_app {
+ name: "SuspendTestApp",
+
+ test_suites: ["device-tests"],
+
+ static_libs: [
+ "androidx.test.runner",
+ "ub-uiautomator",
+ ],
+
+ srcs: [
+ "**/*.java",
+ ":servicestests-SuspendTestApp-files",
+ ],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+
+ platform_apis: true,
+
+}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
deleted file mode 100644
index ab222b9..0000000
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
-
-LOCAL_PACKAGE_NAME := SuspendTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
new file mode 100644
index 0000000..c2cb6881
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 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.
+
+java_library {
+ name: "ShortcutManagerTestUtils",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "mockito-target",
+ "android.test.runner.stubs",
+ ],
+
+ sdk_version: "test_current",
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
deleted file mode 100644
index 019bcbd3..0000000
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
- mockito-target \
- android.test.runner.stubs
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ShortcutManagerTestUtils
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
new file mode 100644
index 0000000..ca8cc0d
--- /dev/null
+++ b/services/tests/uiservicestests/Android.bp
@@ -0,0 +1,58 @@
+//########################################################################
+// Build FrameworksUiServicesTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksUiServicesTests",
+
+ // Include test java files
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "services.accessibility",
+ "services.core",
+ "services.devicepolicy",
+ "services.net",
+ "services.usage",
+ "guava",
+ "android-support-test",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ dxflags: ["--multi-dex"],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ compile_multilib: "both",
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcutils",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdaidl",
+ "libui",
+ "libunwindstack",
+ "libutils",
+ ],
+}
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
deleted file mode 100644
index f3f4355..0000000
--- a/services/tests/uiservicestests/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-#########################################################################
-# Build FrameworksUiServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include test java files and source from notifications package.
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(call all-java-files-under, ../../core/java/com/android/server/notification) \
- $(call all-java-files-under, ../../core/java/com/android/server/slice) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- services.accessibility \
- services.core \
- services.devicepolicy \
- services.net \
- services.usage \
- guava \
- android-support-test \
- mockito-target-inline-minus-junit4 \
- platform-test-annotations \
- testables
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MULTILIB := both
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libmultiplejvmtiagentsinterferenceagent \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcutils \
- liblog \
- liblzma \
- libnativehelper \
- libnetdaidl \
- libui \
- libunwindstack \
- libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_PACKAGE)
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..f11492a 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
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
new file mode 100644
index 0000000..cdba9a1
--- /dev/null
+++ b/services/tests/wmtests/Android.bp
@@ -0,0 +1,52 @@
+//########################################################################
+// Build WmTests package
+//########################################################################
+
+android_test {
+ name: "WmTests",
+
+ // We only want this apk build for tests.
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "servicestests-utils",
+ "truth-prebuilt",
+ "testables",
+ "ub-uiautomator",
+ "hamcrest-library",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ // These are not normally accessible from apps so they must be explicitly included.
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ certificate: "platform",
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+
+}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
deleted file mode 100644
index 67c2860..0000000
--- a/services/tests/wmtests/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-#########################################################################
-# Build WmTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, ../servicestests/utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
- services.core \
- androidx.test.runner \
- androidx.test.rules \
- mockito-target-extended-minus-junit4 \
- platform-test-annotations \
- truth-prebuilt \
- testables \
- ub-uiautomator \
- hamcrest-library
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.mock \
- android.test.base \
- android.test.runner \
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- libdexmakerjvmtiagent \
- libstaticjvmtiagent \
-
-LOCAL_PACKAGE_NAME := WmTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
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/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/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index 03c0b9a..afe91cd 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -145,6 +145,13 @@
if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
continue
+ misspelled_attributes = [
+ ("translateable", "translatable"),
+ ]
+ for misspelling, expected in misspelled_attributes:
+ if misspelling in child.attrib:
+ error(child, "Misspelled <string> attribute.", misspelling, expected)
+
limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
if limit is None:
info(child, "Missing CHAR LIMIT to aid translation",
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();
+ }
}