Merge "Fixed pulsing notifications and flickers with the scrim"
diff --git a/Android.bp b/Android.bp
index a667c10..aa47d7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -307,6 +307,7 @@
"android.hardware.thermal-V1.1-java",
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.0-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb-V1.2-java-constants",
@@ -1440,29 +1441,6 @@
" --show-annotation android.annotation.TestApi ",
}
-filegroup {
- name: "apache-http-stubs-sources",
- srcs: [
- "core/java/org/apache/http/conn/ConnectTimeoutException.java",
- "core/java/org/apache/http/conn/scheme/HostNameResolver.java",
- "core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java",
- "core/java/org/apache/http/conn/scheme/SocketFactory.java",
- "core/java/org/apache/http/conn/ssl/AbstractVerifier.java",
- "core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java",
- "core/java/org/apache/http/conn/ssl/AndroidDistinguishedNameParser.java",
- "core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java",
- "core/java/org/apache/http/conn/ssl/SSLSocketFactory.java",
- "core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java",
- "core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java",
- "core/java/org/apache/http/params/CoreConnectionPNames.java",
- "core/java/org/apache/http/params/HttpConnectionParams.java",
- "core/java/org/apache/http/params/HttpParams.java",
- "core/java/android/net/http/SslCertificate.java",
- "core/java/android/net/http/SslError.java",
- "core/java/com/android/internal/util/HexDump.java",
- ],
-}
-
droidstubs {
name: "api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
diff --git a/api/current.txt b/api/current.txt
index 57bbd77..2cf1f44 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9467,6 +9467,7 @@
method @NonNull public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
+ method @Nullable public final String getCallingFeatureId();
method @Nullable public final String getCallingPackage();
method @Nullable public final android.content.Context getContext();
method @Nullable public final android.content.pm.PathPermission[] getPathPermissions();
@@ -30036,7 +30037,7 @@
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(String, boolean);
method @Deprecated public boolean setWifiEnabled(boolean);
- method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler);
+ method @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler);
method @Deprecated public boolean startScan();
method @Deprecated public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method @Deprecated public int updateNetwork(android.net.wifi.WifiConfiguration);
@@ -35505,7 +35506,7 @@
method public String getUserName();
method public java.util.List<android.os.UserHandle> getUserProfiles();
method public android.os.Bundle getUserRestrictions();
- method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
method public boolean isQuietModeEnabled(android.os.UserHandle);
@@ -38490,7 +38491,11 @@
method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
+ method @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
+ method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
+ method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
+ method public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri);
field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
field public static final String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -38583,14 +38588,11 @@
}
public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
- field public static final String ALBUM = "album";
field public static final String ALBUM_ID = "album_id";
field @Deprecated public static final String ALBUM_KEY = "album_key";
- field public static final String ARTIST = "artist";
field public static final String ARTIST_ID = "artist_id";
field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String BOOKMARK = "bookmark";
- field public static final String COMPOSER = "composer";
field public static final String GENRE = "genre";
field public static final String GENRE_ID = "genre_id";
field @Deprecated public static final String GENRE_KEY = "genre_key";
@@ -38705,7 +38707,6 @@
field public static final int MEDIA_TYPE_VIDEO = 3; // 0x3
field public static final String MIME_TYPE = "mime_type";
field public static final String PARENT = "parent";
- field public static final String TITLE = "title";
}
public static final class MediaStore.Images {
@@ -38714,6 +38715,9 @@
public static interface MediaStore.Images.ImageColumns extends android.provider.MediaStore.MediaColumns {
field public static final String DESCRIPTION = "description";
+ field public static final String EXPOSURE_TIME = "exposure_time";
+ field public static final String F_NUMBER = "f_number";
+ field public static final String ISO = "iso";
field public static final String IS_PRIVATE = "isprivate";
field @Deprecated public static final String LATITUDE = "latitude";
field @Deprecated public static final String LONGITUDE = "longitude";
@@ -38762,28 +38766,45 @@
}
public static interface MediaStore.MediaColumns extends android.provider.BaseColumns {
+ field public static final String ALBUM = "album";
+ field public static final String ALBUM_ARTIST = "album_artist";
+ field public static final String ARTIST = "artist";
+ field public static final String AUTHOR = "author";
+ field public static final String BITRATE = "bitrate";
field public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
field public static final String BUCKET_ID = "bucket_id";
+ field public static final String CAPTURE_FRAMERATE = "capture_framerate";
+ field public static final String CD_TRACK_NUMBER = "cd_track_number";
+ field public static final String COMPILATION = "compilation";
+ field public static final String COMPOSER = "composer";
field @Deprecated public static final String DATA = "_data";
field public static final String DATE_ADDED = "date_added";
field public static final String DATE_EXPIRES = "date_expires";
field public static final String DATE_MODIFIED = "date_modified";
field public static final String DATE_TAKEN = "datetaken";
+ field public static final String DISC_NUMBER = "disc_number";
field public static final String DISPLAY_NAME = "_display_name";
field public static final String DOCUMENT_ID = "document_id";
field public static final String DURATION = "duration";
+ field public static final String GENRE = "genre";
field public static final String HEIGHT = "height";
field public static final String INSTANCE_ID = "instance_id";
+ field public static final String IS_FAVORITE = "is_favorite";
field public static final String IS_PENDING = "is_pending";
+ field public static final String IS_TRASHED = "is_trashed";
field public static final String MIME_TYPE = "mime_type";
+ field public static final String NUM_TRACKS = "num_tracks";
field public static final String ORIENTATION = "orientation";
field public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
field public static final String OWNER_PACKAGE_NAME = "owner_package_name";
field public static final String RELATIVE_PATH = "relative_path";
+ field public static final String RESOLUTION = "resolution";
field public static final String SIZE = "_size";
field public static final String TITLE = "title";
field public static final String VOLUME_NAME = "volume_name";
field public static final String WIDTH = "width";
+ field public static final String WRITER = "writer";
+ field public static final String YEAR = "year";
}
public static final class MediaStore.Video {
@@ -38823,8 +38844,6 @@
}
public static interface MediaStore.Video.VideoColumns extends android.provider.MediaStore.MediaColumns {
- field public static final String ALBUM = "album";
- field public static final String ARTIST = "artist";
field public static final String BOOKMARK = "bookmark";
field public static final String CATEGORY = "category";
field public static final String COLOR_RANGE = "color_range";
@@ -38836,7 +38855,6 @@
field @Deprecated public static final String LATITUDE = "latitude";
field @Deprecated public static final String LONGITUDE = "longitude";
field @Deprecated public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
- field public static final String RESOLUTION = "resolution";
field public static final String TAGS = "tags";
}
@@ -44380,6 +44398,7 @@
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+ field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
diff --git a/api/removed.txt b/api/removed.txt
index 74a9346..a395cc7 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -444,13 +444,12 @@
method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams);
method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context);
method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri);
- method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
- method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
- method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
- method @Deprecated public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri);
}
public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
+ field public static final String ALBUM = "album";
+ field public static final String ARTIST = "artist";
+ field public static final String COMPOSER = "composer";
field public static final String DURATION = "duration";
}
@@ -458,6 +457,10 @@
field @Deprecated public static final String DESCRIPTION = "description";
}
+ public static interface MediaStore.Files.FileColumns extends android.provider.MediaStore.MediaColumns {
+ field public static final String TITLE = "title";
+ }
+
public static interface MediaStore.Images.ImageColumns extends android.provider.MediaStore.MediaColumns {
field public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
field public static final String BUCKET_ID = "bucket_id";
@@ -468,10 +471,6 @@
public static interface MediaStore.MediaColumns extends android.provider.BaseColumns {
field @Deprecated public static final String GROUP_ID = "group_id";
- field @Deprecated public static final String HASH = "_hash";
- field @Deprecated public static final String IS_TRASHED = "is_trashed";
- field @Deprecated public static final String PRIMARY_DIRECTORY = "primary_directory";
- field @Deprecated public static final String SECONDARY_DIRECTORY = "secondary_directory";
}
@Deprecated public static class MediaStore.PendingParams {
@@ -491,11 +490,14 @@
}
public static interface MediaStore.Video.VideoColumns extends android.provider.MediaStore.MediaColumns {
+ field public static final String ALBUM = "album";
+ field public static final String ARTIST = "artist";
field public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
field public static final String BUCKET_ID = "bucket_id";
field public static final String DATE_TAKEN = "datetaken";
field public static final String DURATION = "duration";
field public static final String GROUP_ID = "group_id";
+ field public static final String RESOLUTION = "resolution";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
diff --git a/api/system-current.txt b/api/system-current.txt
index e6a3e9b..1a6ac8d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3480,7 +3480,8 @@
method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean);
method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean);
method public int describeContents();
- method public long getExpireAt();
+ method @Deprecated public long getExpireAt();
+ method public long getExpireIn();
method public long getFastestInterval();
method public boolean getHideFromAppOps();
method public long getInterval();
@@ -3491,7 +3492,7 @@
method @Nullable public android.os.WorkSource getWorkSource();
method public boolean isLocationSettingsIgnored();
method public boolean isLowPowerMode();
- method @NonNull public android.location.LocationRequest setExpireAt(long);
+ method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
method @NonNull public android.location.LocationRequest setExpireIn(long);
method @NonNull public android.location.LocationRequest setFastestInterval(long);
method public void setHideFromAppOps(boolean);
@@ -4734,6 +4735,24 @@
field @Deprecated public byte id;
}
+ public final class SoftApConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.MacAddress getBssid();
+ method @Nullable public String getSsid();
+ method @Nullable public String getWpa2Passphrase();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
+ }
+
+ public static final class SoftApConfiguration.Builder {
+ ctor public SoftApConfiguration.Builder();
+ ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
+ method @NonNull public android.net.wifi.SoftApConfiguration build();
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
+ }
+
public final class WifiClient implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.net.MacAddress getMacAddress();
@@ -4746,6 +4765,7 @@
method @Deprecated public boolean isEphemeral();
method @Deprecated public boolean isNoInternetAccessExpected();
field @Deprecated public boolean allowAutojoin;
+ field @Deprecated public int carrierId;
field @Deprecated public String creatorName;
field @Deprecated public int creatorUid;
field @Deprecated public String lastUpdateName;
@@ -4789,6 +4809,7 @@
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
@@ -4852,10 +4873,14 @@
field public int numUsage;
}
+ public static final class WifiNetworkSuggestion.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
+ }
+
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
- method @Nullable @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults();
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
@@ -5666,6 +5691,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
method public boolean hasRestrictedProfiles();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isGuestUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile();
@@ -6246,15 +6272,21 @@
field public static final String DELIVERY_TIME = "date";
field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
field public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
+ field public static final String GEOMETRIES = "geometries";
field public static final String LAC = "lac";
field public static final String LANGUAGE_CODE = "language";
+ field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
field public static final String MESSAGE_BODY = "body";
+ field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
field public static final String MESSAGE_FORMAT = "format";
+ field @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) @NonNull public static final android.net.Uri MESSAGE_HISTORY_URI;
field public static final String MESSAGE_PRIORITY = "priority";
field public static final String MESSAGE_READ = "read";
field public static final String PLMN = "plmn";
+ field public static final String RECEIVED_TIME = "received_time";
field public static final String SERIAL_NUMBER = "serial_number";
field public static final String SERVICE_CATEGORY = "service_category";
+ field public static final String SLOT_INDEX = "slot_index";
}
public final class TimeZoneRulesDataContract {
@@ -6532,6 +6564,28 @@
method @WorkerThread @NonNull public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
}
+ public abstract class CarrierMessagingServiceWrapper {
+ ctor public CarrierMessagingServiceWrapper();
+ method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String);
+ method public void disposeConnection(@NonNull android.content.Context);
+ method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
+ method public void filterSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
+ method public abstract void onServiceReady();
+ method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
+ method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
+ method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
+ method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
+ }
+
+ public abstract static class CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
+ ctor public CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper();
+ method public void onDownloadMmsComplete(int);
+ method public void onFilterComplete(int);
+ method public void onSendMmsComplete(int, @Nullable byte[]);
+ method public void onSendMultipartSmsComplete(int, @Nullable int[]);
+ method public void onSendSmsComplete(int, int);
+ }
+
}
package android.service.contentcapture {
@@ -7431,6 +7485,7 @@
ctor public CellBroadcastService();
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
method public abstract void onCdmaCellBroadcastSms(int, byte[], int);
+ method public abstract void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public abstract void onGsmCellBroadcastSms(int, byte[]);
field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
}
@@ -8343,6 +8398,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
+ method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset(int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
@@ -9010,17 +9066,23 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
- public class ImsMmTelManager {
+ public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.function.Consumer<java.lang.Boolean>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTtyOverVolteEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
@@ -9030,7 +9092,8 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
@@ -9042,12 +9105,8 @@
method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
}
- public static class ImsMmTelManager.RegistrationCallback {
- ctor public ImsMmTelManager.RegistrationCallback();
- method public void onRegistered(int);
- method public void onRegistering(int);
- method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
+ ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
}
public final class ImsReasonInfo implements android.os.Parcelable {
@@ -9479,6 +9538,24 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
+ public interface RegistrationManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
+ field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
+ field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
+ }
+
+ public static class RegistrationManager.RegistrationCallback {
+ ctor public RegistrationManager.RegistrationCallback();
+ method public void onRegistered(int);
+ method public void onRegistering(int);
+ method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ }
+
}
package android.telephony.ims.feature {
@@ -9543,6 +9620,8 @@
method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
method public void setUiTtyMode(int, @Nullable android.os.Message);
method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]);
+ field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+ field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
field public static final int PROCESS_CALL_CSFB = 1; // 0x1
field public static final int PROCESS_CALL_IMS = 0; // 0x0
}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 57a853a..a907fa6 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -257,6 +257,8 @@
SamShouldBeLast: android.net.ConnectivityManager#createSocketKeepalive(android.net.Network, android.net.IpSecManager.UdpEncapsulationSocket, java.net.InetAddress, java.net.InetAddress, java.util.concurrent.Executor, android.net.SocketKeepalive.Callback):
+SamShouldBeLast: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.SoftApConfiguration, java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyHotspotCallback):
+
SamShouldBeLast: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle):
diff --git a/api/test-current.txt b/api/test-current.txt
index a060dfc..5b16743 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1136,17 +1136,20 @@
public final class LocationRequest implements android.os.Parcelable {
method @NonNull public static android.location.LocationRequest create();
method public int describeContents();
- method public long getExpireAt();
+ method @Deprecated public long getExpireAt();
+ method public long getExpireIn();
method public long getFastestInterval();
method public long getInterval();
method public int getNumUpdates();
method public int getQuality();
method public boolean isLocationSettingsIgnored();
- method @NonNull public android.location.LocationRequest setExpireAt(long);
+ method public boolean isLowPowerMode();
+ method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
method @NonNull public android.location.LocationRequest setExpireIn(long);
method @NonNull public android.location.LocationRequest setFastestInterval(long);
method @NonNull public android.location.LocationRequest setInterval(long);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @NonNull public android.location.LocationRequest setLocationSettingsIgnored(boolean);
+ method @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
method @NonNull public android.location.LocationRequest setNumUpdates(int);
method @NonNull public android.location.LocationRequest setProvider(@NonNull String);
method @NonNull public android.location.LocationRequest setQuality(int);
@@ -2063,6 +2066,8 @@
method @NonNull public static String get(@NonNull String);
method @NonNull public static String get(@NonNull String, @Nullable String);
method public static boolean getBoolean(@NonNull String, boolean);
+ method public static int getInt(@NonNull String, int);
+ method public static long getLong(@NonNull String, long);
}
public final class UserHandle implements android.os.Parcelable {
@@ -2456,6 +2461,36 @@
field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
}
+ public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns {
+ field public static final String CID = "cid";
+ field public static final String CMAS_CATEGORY = "cmas_category";
+ field public static final String CMAS_CERTAINTY = "cmas_certainty";
+ field public static final String CMAS_MESSAGE_CLASS = "cmas_message_class";
+ field public static final String CMAS_RESPONSE_TYPE = "cmas_response_type";
+ field public static final String CMAS_SEVERITY = "cmas_severity";
+ field public static final String CMAS_URGENCY = "cmas_urgency";
+ field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final String DEFAULT_SORT_ORDER = "date DESC";
+ field public static final String DELIVERY_TIME = "date";
+ field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
+ field public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
+ field public static final String GEOMETRIES = "geometries";
+ field public static final String LAC = "lac";
+ field public static final String LANGUAGE_CODE = "language";
+ field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
+ field public static final String MESSAGE_BODY = "body";
+ field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
+ field public static final String MESSAGE_FORMAT = "format";
+ field @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) @NonNull public static final android.net.Uri MESSAGE_HISTORY_URI;
+ field public static final String MESSAGE_PRIORITY = "priority";
+ field public static final String MESSAGE_READ = "read";
+ field public static final String PLMN = "plmn";
+ field public static final String RECEIVED_TIME = "received_time";
+ field public static final String SERIAL_NUMBER = "serial_number";
+ field public static final String SERVICE_CATEGORY = "service_category";
+ field public static final String SLOT_INDEX = "slot_index";
+ }
+
public static final class Telephony.Sms.Intents {
field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION";
}
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 55dbc17..7e278e9 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -508,7 +508,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(resolveCallingPackage(), mUri, mContentValues);
+ provider.insert(resolveCallingPackage(), null, mUri, mContentValues);
}
}
@@ -522,7 +522,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(resolveCallingPackage(), mUri, mWhere, null);
+ provider.delete(resolveCallingPackage(), null, mUri, mWhere, null);
}
}
@@ -557,7 +557,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras);
+ Bundle result = provider.call(null, null, mUri.getAuthority(), mMethod, mArg, mExtras);
if (result != null) {
result.size(); // unpack
}
@@ -584,7 +584,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "r", null, null)) {
FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
@@ -597,7 +597,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "w", null, null)) {
FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
@@ -616,7 +616,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection,
+ Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection,
ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
if (cursor == null) {
System.out.println("No result found.");
@@ -679,7 +679,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(resolveCallingPackage(), mUri, mContentValues, mWhere, null);
+ provider.update(resolveCallingPackage(), null, mUri, mContentValues, mWhere, null);
}
}
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 455e4bb..b23bf5d 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -67,7 +67,7 @@
throw new IllegalStateException("Could not find provider: " + providerName);
}
provider = holder.provider;
- cursor = provider.query(null, Settings.Secure.CONTENT_URI,
+ cursor = provider.query(null, null, Settings.Secure.CONTENT_URI,
new String[] {
Settings.Secure.VALUE
},
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 7de8793..17f1a07 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -53,6 +53,7 @@
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -136,7 +137,7 @@
private boolean mNoPerms;
private boolean mSingleUser;
- private ThreadLocal<String> mCallingPackage;
+ private ThreadLocal<Pair<String, String>> mCallingPackage;
private Transport mTransport = new Transport();
@@ -226,11 +227,13 @@
}
@Override
- public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
+ public Cursor query(String callingPkg, @Nullable String featureId, Uri uri,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
// the columns in the requested order. The caller may ask for an invalid
// column and we would not catch that but this is not a problem in practice.
@@ -246,7 +249,8 @@
// we have to execute the query as if allowed to get a cursor with the
// columns. We then use the column names to return an empty cursor.
Cursor cursor;
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
cursor = mInterface.query(
uri, projection, queryArgs,
@@ -264,7 +268,8 @@
return new MatrixCursor(cursor.getColumnNames(), 0);
}
Trace.traceBegin(TRACE_TAG_DATABASE, "query");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.query(
uri, projection, queryArgs,
@@ -293,12 +298,15 @@
}
@Override
- public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
+ public Uri insert(String callingPkg, @Nullable String featureId, Uri uri,
+ ContentValues initialValues) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- final String original = setCallingPackage(callingPkg);
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return rejectInsert(uri, initialValues);
} finally {
@@ -306,7 +314,8 @@
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return maybeAddUserId(mInterface.insert(uri, initialValues), userId);
} catch (RemoteException e) {
@@ -318,14 +327,17 @@
}
@Override
- public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
+ public int bulkInsert(String callingPkg, @Nullable String featureId, Uri uri,
+ ContentValues[] initialValues) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.bulkInsert(uri, initialValues);
} catch (RemoteException e) {
@@ -337,8 +349,8 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
validateIncomingAuthority(authority);
int numOperations = operations.size();
@@ -355,20 +367,21 @@
operations.set(i, operation);
}
if (operation.isReadOperation()) {
- if (enforceReadPermission(callingPkg, uri, null)
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
if (operation.isWriteOperation()) {
- if (enforceWritePermission(callingPkg, uri, null)
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
ContentProviderResult[] results = mInterface.applyBatch(authority,
operations);
@@ -390,14 +403,17 @@
}
@Override
- public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
+ public int delete(String callingPkg, @Nullable String featureId, Uri uri, String selection,
+ String[] selectionArgs) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.delete(uri, selection, selectionArgs);
} catch (RemoteException e) {
@@ -409,15 +425,17 @@
}
@Override
- public int update(String callingPkg, Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
+ public int update(String callingPkg, @Nullable String featureId, Uri uri,
+ ContentValues values, String selection, String[] selectionArgs) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "update");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.update(uri, values, selection, selectionArgs);
} catch (RemoteException e) {
@@ -429,14 +447,15 @@
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
- IBinder callerToken) throws FileNotFoundException {
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId,
+ Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
+ throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, mode, callerToken);
+ enforceFilePermission(callingPkg, featureId, uri, mode, callerToken);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.openFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -449,14 +468,15 @@
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, mode, null);
+ enforceFilePermission(callingPkg, featureId, uri, mode, null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.openAssetFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -469,12 +489,13 @@
}
@Override
- public Bundle call(String callingPkg, String authority, String method, @Nullable String arg,
- @Nullable Bundle extras) {
+ public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.call(authority, method, arg, extras);
} catch (RemoteException e) {
@@ -501,14 +522,16 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
- Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg,
+ @Nullable String featureId, Uri uri, String mimeType, Bundle opts,
+ ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, "r", null);
+ enforceFilePermission(callingPkg, featureId, uri, "r", null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.openTypedAssetFile(
uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
@@ -526,15 +549,17 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri uri) {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return maybeAddUserId(mInterface.canonicalize(uri), userId);
} catch (RemoteException e) {
@@ -546,15 +571,17 @@
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri uri) {
+ public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
} catch (RemoteException e) {
@@ -566,15 +593,17 @@
}
@Override
- public boolean refresh(String callingPkg, Uri uri, Bundle args,
+ public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return false;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.refresh(uri, args,
CancellationSignal.fromTransport(cancellationSignal));
@@ -585,11 +614,13 @@
}
@Override
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.checkUriPermission(uri, uid, modeFlags);
} catch (RemoteException e) {
@@ -600,44 +631,47 @@
}
}
- private void enforceFilePermission(String callingPkg, Uri uri, String mode,
- IBinder callerToken) throws FileNotFoundException, SecurityException {
+ private void enforceFilePermission(String callingPkg, @Nullable String featureId, Uri uri,
+ String mode, IBinder callerToken) throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
- if (enforceWritePermission(callingPkg, uri, callerToken)
+ if (enforceWritePermission(callingPkg, featureId, uri, callerToken)
!= AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
} else {
- if (enforceReadPermission(callingPkg, uri, callerToken)
+ if (enforceReadPermission(callingPkg, featureId, uri, callerToken)
!= AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
}
}
- private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
+ private int enforceReadPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ IBinder callerToken)
throws SecurityException {
- final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken);
+ final int mode = enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
if (mode != MODE_ALLOWED) {
return mode;
}
- return noteProxyOp(callingPkg, mReadOp);
+ return noteProxyOp(callingPkg, featureId, mReadOp);
}
- private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
+ private int enforceWritePermission(String callingPkg, String featureId, Uri uri,
+ IBinder callerToken)
throws SecurityException {
- final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken);
+ final int mode = enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
if (mode != MODE_ALLOWED) {
return mode;
}
- return noteProxyOp(callingPkg, mWriteOp);
+ return noteProxyOp(callingPkg, featureId, mWriteOp);
}
- private int noteProxyOp(String callingPkg, int op) {
+ private int noteProxyOp(String callingPkg, String featureId, int op) {
if (op != AppOpsManager.OP_NONE) {
- int mode = mAppOpsManager.noteProxyOp(op, callingPkg);
+ int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
+ featureId, null);
return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
}
@@ -659,18 +693,19 @@
* associated with that permission.
*/
private int checkPermissionAndAppOp(String permission, String callingPkg,
- IBinder callerToken) {
+ @Nullable String featureId, IBinder callerToken) {
if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
callerToken) != PERMISSION_GRANTED) {
return MODE_ERRORED;
}
- return mTransport.noteProxyOp(callingPkg, AppOpsManager.permissionToOpCode(permission));
+ return mTransport.noteProxyOp(callingPkg, featureId,
+ AppOpsManager.permissionToOpCode(permission));
}
/** {@hide} */
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -684,7 +719,8 @@
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -703,7 +739,8 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -751,8 +788,8 @@
}
/** {@hide} */
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -766,7 +803,8 @@
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -785,7 +823,8 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -851,11 +890,11 @@
}
/**
- * Set the calling package, returning the current value (or {@code null})
+ * Set the calling package/feature, returning the current value (or {@code null})
* which can be used later to restore the previous state.
*/
- private String setCallingPackage(String callingPackage) {
- final String original = mCallingPackage.get();
+ private Pair<String, String> setCallingPackage(Pair<String, String> callingPackage) {
+ final Pair<String, String> original = mCallingPackage.get();
mCallingPackage.set(callingPackage);
onCallingPackageChanged();
return original;
@@ -876,16 +915,42 @@
* calling UID.
*/
public final @Nullable String getCallingPackage() {
- final String pkg = mCallingPackage.get();
+ final Pair<String, String> pkg = mCallingPackage.get();
if (pkg != null) {
- mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
+ mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg.first);
+ return pkg.first;
}
- return pkg;
+
+ return null;
+ }
+
+ /**
+ * Return the feature in the package of the caller that initiated the request being
+ * processed on the current thread. Returns {@code null} if not currently processing
+ * a request of the request is for the default feature.
+ * <p>
+ * This will always return {@code null} when processing
+ * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+ *
+ * @see #getCallingPackage
+ */
+ public final @Nullable String getCallingFeatureId() {
+ final Pair<String, String> pkg = mCallingPackage.get();
+ if (pkg != null) {
+ return pkg.second;
+ }
+
+ return null;
}
/** {@hide} */
public final @Nullable String getCallingPackageUnchecked() {
- return mCallingPackage.get();
+ final Pair<String, String> pkg = mCallingPackage.get();
+ if (pkg != null) {
+ return pkg.first;
+ }
+
+ return null;
}
/** {@hide} */
@@ -899,10 +964,10 @@
/** {@hide} */
public final long binderToken;
/** {@hide} */
- public final String callingPackage;
+ public final Pair<String, String> callingPackage;
/** {@hide} */
- public CallingIdentity(long binderToken, String callingPackage) {
+ public CallingIdentity(long binderToken, Pair<String, String> callingPackage) {
this.binderToken = binderToken;
this.callingPackage = callingPackage;
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 8a4330e..d2632e7 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -80,6 +80,7 @@
private final IContentProvider mContentProvider;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
+ private final @Nullable String mFeatureId;
private final String mAuthority;
private final boolean mStable;
@@ -103,6 +104,7 @@
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
+ mFeatureId = contentResolver.mFeatureId;
mAuthority = authority;
mStable = stable;
@@ -193,7 +195,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
final Cursor cursor = mContentProvider.query(
- mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal);
if (cursor == null) {
return null;
}
@@ -253,7 +255,7 @@
beforeRemote();
try {
- return mContentProvider.canonicalize(mPackageName, url);
+ return mContentProvider.canonicalize(mPackageName, mFeatureId, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -271,7 +273,7 @@
beforeRemote();
try {
- return mContentProvider.uncanonicalize(mPackageName, url);
+ return mContentProvider.uncanonicalize(mPackageName, mFeatureId, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -296,7 +298,8 @@
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal);
+ return mContentProvider.refresh(mPackageName, mFeatureId, url, args,
+ remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -315,7 +318,8 @@
beforeRemote();
try {
- return mContentProvider.checkUriPermission(mPackageName, uri, uid, modeFlags);
+ return mContentProvider.checkUriPermission(mPackageName, mFeatureId, uri, uid,
+ modeFlags);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -334,7 +338,7 @@
beforeRemote();
try {
- return mContentProvider.insert(mPackageName, url, initialValues);
+ return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -354,7 +358,7 @@
beforeRemote();
try {
- return mContentProvider.bulkInsert(mPackageName, url, initialValues);
+ return mContentProvider.bulkInsert(mPackageName, mFeatureId, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -373,7 +377,8 @@
beforeRemote();
try {
- return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
+ return mContentProvider.delete(mPackageName, mFeatureId, url, selection,
+ selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -392,7 +397,8 @@
beforeRemote();
try {
- return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
+ return mContentProvider.update(mPackageName, mFeatureId, url, values, selection,
+ selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -436,7 +442,8 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
+ return mContentProvider.openFile(mPackageName, mFeatureId, url, mode, remoteSignal,
+ null);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -480,7 +487,8 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
+ return mContentProvider.openAssetFile(mPackageName, mFeatureId, url, mode,
+ remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -521,7 +529,7 @@
signal.setRemote(remoteSignal);
}
return mContentProvider.openTypedAssetFile(
- mPackageName, uri, mimeTypeFilter, opts, remoteSignal);
+ mPackageName, mFeatureId, uri, mimeTypeFilter, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -548,7 +556,7 @@
beforeRemote();
try {
- return mContentProvider.applyBatch(mPackageName, authority, operations);
+ return mContentProvider.applyBatch(mPackageName, mFeatureId, authority, operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -574,7 +582,7 @@
beforeRemote();
try {
- return mContentProvider.call(mPackageName, authority, method, arg, extras);
+ return mContentProvider.call(mPackageName, mFeatureId, authority, method, arg, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index cd735d4..f082690 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -83,6 +83,7 @@
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String callingFeatureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
@@ -101,7 +102,8 @@
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);
+ Cursor cursor = query(callingPkg, callingFeatureId, url, projection, queryArgs,
+ cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
@@ -148,10 +150,11 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
- Uri out = insert(callingPkg, url, values);
+ Uri out = insert(callingPkg, featureId, url, values);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -161,10 +164,11 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
- int count = bulkInsert(callingPkg, url, values);
+ int count = bulkInsert(callingPkg, featureId, url, values);
reply.writeNoException();
reply.writeInt(count);
return true;
@@ -174,6 +178,7 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
String authority = data.readString();
final int numOperations = data.readInt();
final ArrayList<ContentProviderOperation> operations =
@@ -181,8 +186,8 @@
for (int i = 0; i < numOperations; i++) {
operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
}
- final ContentProviderResult[] results = applyBatch(callingPkg, authority,
- operations);
+ final ContentProviderResult[] results = applyBatch(callingPkg, featureId,
+ authority, operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
return true;
@@ -192,11 +197,12 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String selection = data.readString();
String[] selectionArgs = data.readStringArray();
- int count = delete(callingPkg, url, selection, selectionArgs);
+ int count = delete(callingPkg, featureId, url, selection, selectionArgs);
reply.writeNoException();
reply.writeInt(count);
@@ -207,12 +213,14 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
String selection = data.readString();
String[] selectionArgs = data.readStringArray();
- int count = update(callingPkg, url, values, selection, selectionArgs);
+ int count = update(callingPkg, featureId, url, values, selection,
+ selectionArgs);
reply.writeNoException();
reply.writeInt(count);
@@ -223,6 +231,7 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
@@ -230,7 +239,7 @@
IBinder callerToken = data.readStrongBinder();
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, url, mode, signal, callerToken);
+ fd = openFile(callingPkg, featureId, url, mode, signal, callerToken);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -246,13 +255,14 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openAssetFile(callingPkg, url, mode, signal);
+ fd = openAssetFile(callingPkg, featureId, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -269,12 +279,14 @@
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
String authority = data.readString();
String method = data.readString();
String stringArg = data.readString();
Bundle args = data.readBundle();
- Bundle responseBundle = call(callingPkg, authority, method, stringArg, args);
+ Bundle responseBundle = call(callingPkg, featureId, authority, method,
+ stringArg, args);
reply.writeNoException();
reply.writeBundle(responseBundle);
@@ -297,6 +309,7 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
@@ -304,7 +317,7 @@
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openTypedAssetFile(callingPkg, url, mimeType, opts, signal);
+ fd = openTypedAssetFile(callingPkg, featureId, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -330,9 +343,10 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = canonicalize(callingPkg, url);
+ Uri out = canonicalize(callingPkg, featureId, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -342,9 +356,10 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = uncanonicalize(callingPkg, url);
+ Uri out = uncanonicalize(callingPkg, featureId, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -353,12 +368,13 @@
case REFRESH_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle args = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- boolean out = refresh(callingPkg, url, args, signal);
+ boolean out = refresh(callingPkg, featureId, url, args, signal);
reply.writeNoException();
reply.writeInt(out ? 0 : -1);
return true;
@@ -367,11 +383,12 @@
case CHECK_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri uri = Uri.CREATOR.createFromParcel(data);
int uid = data.readInt();
int modeFlags = data.readInt();
- int out = checkUriPermission(callingPkg, uri, uid, modeFlags);
+ int out = checkUriPermission(callingPkg, featureId, uri, uid, modeFlags);
reply.writeNoException();
reply.writeInt(out);
return true;
@@ -407,8 +424,9 @@
}
@Override
- public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
+ public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
@@ -417,6 +435,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
@@ -478,7 +497,8 @@
}
@Override
- public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException
+ public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
+ ContentValues values) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -486,6 +506,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
@@ -501,13 +522,15 @@
}
@Override
- public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException {
+ public int bulkInsert(String callingPkg, @Nullable String featureId, Uri url,
+ ContentValues[] values) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeTypedArray(values, 0);
@@ -523,14 +546,15 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, String authority,
- ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
data.writeString(authority);
data.writeInt(operations.size());
for (ContentProviderOperation operation : operations) {
@@ -549,14 +573,15 @@
}
@Override
- public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int delete(String callingPkg, @Nullable String featureId, Uri url, String selection,
+ String[] selectionArgs) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(selection);
data.writeStringArray(selectionArgs);
@@ -573,14 +598,15 @@
}
@Override
- public int update(String callingPkg, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int update(String callingPkg, @Nullable String featureId, Uri url,
+ ContentValues values, String selection, String[] selectionArgs) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeString(selection);
@@ -598,8 +624,8 @@
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal, IBinder token)
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
+ String mode, ICancellationSignal signal, IBinder token)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -607,6 +633,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -626,8 +653,8 @@
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal)
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -635,6 +662,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -653,14 +681,15 @@
}
@Override
- public Bundle call(String callingPkg, String authority, String method, String request,
- Bundle args) throws RemoteException {
+ public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ String method, String request, Bundle args) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
data.writeString(authority);
data.writeString(method);
data.writeString(request);
@@ -700,14 +729,16 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
@@ -747,14 +778,15 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri url) throws RemoteException
- {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.CANONICALIZE_TRANSACTION, data, reply, 0);
@@ -769,13 +801,15 @@
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri url) throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.UNCANONICALIZE_TRANSACTION, data, reply, 0);
@@ -790,14 +824,15 @@
}
@Override
- public boolean refresh(String callingPkg, Uri url, Bundle args, ICancellationSignal signal)
- throws RemoteException {
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
+ ICancellationSignal signal) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeBundle(args);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -814,14 +849,15 @@
}
@Override
- public int checkUriPermission(String callingPkg, Uri url, int uid, int modeFlags)
- throws RemoteException {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri url, int uid,
+ int modeFlags) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeInt(uid);
data.writeInt(modeFlags);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7f9ea76..2657cc5 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -649,6 +649,7 @@
public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
mContext = context != null ? context : ActivityThread.currentApplication();
mPackageName = mContext.getOpPackageName();
+ mFeatureId = mContext.getFeatureId();
mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
mWrapped = wrapped;
}
@@ -968,7 +969,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- qCursor = unstableProvider.query(mPackageName, uri, projection,
+ qCursor = unstableProvider.query(mPackageName, mFeatureId, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
@@ -979,8 +980,8 @@
if (stableProvider == null) {
return null;
}
- qCursor = stableProvider.query(
- mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
+ qCursor = stableProvider.query(mPackageName, mFeatureId, uri, projection,
+ queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
@@ -1070,7 +1071,7 @@
}
try {
- return provider.canonicalize(mPackageName, url);
+ return provider.canonicalize(mPackageName, mFeatureId, url);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1114,7 +1115,7 @@
}
try {
- return provider.uncanonicalize(mPackageName, url);
+ return provider.uncanonicalize(mPackageName, mFeatureId, url);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1163,7 +1164,8 @@
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return provider.refresh(mPackageName, url, args, remoteCancellationSignal);
+ return provider.refresh(mPackageName, mFeatureId, url, args,
+ remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1564,7 +1566,7 @@
try {
fd = unstableProvider.openAssetFile(
- mPackageName, uri, mode, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1579,7 +1581,7 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openAssetFile(
- mPackageName, uri, mode, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1730,7 +1732,7 @@
try {
fd = unstableProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1745,7 +1747,7 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1870,7 +1872,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(mPackageName, url, values);
+ Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -1951,7 +1953,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsCreated = provider.bulkInsert(mPackageName, url, values);
+ int rowsCreated = provider.bulkInsert(mPackageName, mFeatureId, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
return rowsCreated;
@@ -1991,7 +1993,8 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs);
+ int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, where,
+ selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
return rowsDeleted;
@@ -2035,7 +2038,8 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
+ int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, where,
+ selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
return rowsUpdated;
@@ -2084,7 +2088,8 @@
throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
- final Bundle res = provider.call(mPackageName, authority, method, arg, extras);
+ final Bundle res = provider.call(mPackageName, mFeatureId, authority, method, arg,
+ extras);
Bundle.setDefusable(res, true);
return res;
} catch (RemoteException e) {
@@ -3436,6 +3441,11 @@
return mPackageName;
}
+ /** @hide */
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
@UnsupportedAppUsage
private static volatile IContentService sContentService;
@UnsupportedAppUsage
@@ -3443,6 +3453,7 @@
@UnsupportedAppUsage
final String mPackageName;
+ final @Nullable String mFeatureId;
final int mTargetSdkVersion;
final ContentInterface mWrapped;
@@ -3638,19 +3649,19 @@
orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
return afd;
}), (ImageDecoder decoder, ImageInfo info, Source source) -> {
- decoder.setAllocator(allocator);
+ decoder.setAllocator(allocator);
- // One last-ditch check to see if we've been canceled.
- if (signal != null) signal.throwIfCanceled();
+ // One last-ditch check to see if we've been canceled.
+ if (signal != null) signal.throwIfCanceled();
- // We requested a rough thumbnail size, but the remote size may have
- // returned something giant, so defensively scale down as needed.
- final int widthSample = info.getSize().getWidth() / size.getWidth();
- final int heightSample = info.getSize().getHeight() / size.getHeight();
- final int sample = Math.min(widthSample, heightSample);
- if (sample > 1) {
- decoder.setTargetSampleSize(sample);
- }
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final int widthSample = info.getSize().getWidth() / size.getWidth();
+ final int heightSample = info.getSize().getHeight() / size.getHeight();
+ final int sample = Math.max(widthSample, heightSample);
+ if (sample > 1) {
+ decoder.setTargetSampleSize(sample);
+ }
});
// Transform the bitmap if requested. We use a side-channel to
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index fade0ab..d2c97c4 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -16,7 +16,6 @@
package android.content;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
@@ -38,66 +37,96 @@
* @hide
*/
public interface IContentProvider extends IInterface {
- public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
+ public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
- @UnsupportedAppUsage
- public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
- throws RemoteException;
- @UnsupportedAppUsage
- public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
- throws RemoteException;
- @UnsupportedAppUsage
- public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
- throws RemoteException;
- @UnsupportedAppUsage
- public int update(String callingPkg, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException;
- public ParcelFileDescriptor openFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal,
- IBinder callerToken)
- throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal)
- throws RemoteException, FileNotFoundException;
-
@Deprecated
- public default ContentProviderResult[] applyBatch(String callingPkg,
- ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
- return applyBatch(callingPkg, "unknown", operations);
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} "
+ + "instead")
+ public default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
+ throws RemoteException {
+ return insert(callingPkg, null, url, initialValues);
}
+ public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues)
+ throws RemoteException;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
+ + "} instead")
+ public default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
+ throws RemoteException {
+ return bulkInsert(callingPkg, null, url, initialValues);
+ }
+ public int bulkInsert(String callingPkg, String featureId, Uri url,
+ ContentValues[] initialValues) throws RemoteException;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#delete(android.net.Uri, java.lang.String, java.lang"
+ + ".String[])} instead")
+ public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
+ throws RemoteException {
+ return delete(callingPkg, null, url, selection, selectionArgs);
+ }
+ public int delete(String callingPkg, String featureId, Uri url, String selection,
+ String[] selectionArgs) throws RemoteException;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java"
+ + ".lang.String, java.lang.String[])} instead")
+ public default int update(String callingPkg, Uri url, ContentValues values, String selection,
+ String[] selectionArgs) throws RemoteException {
+ return update(callingPkg, null, url, values, selection, selectionArgs);
+ }
+ public int update(String callingPkg, String featureId, Uri url, ContentValues values,
+ String selection, String[] selectionArgs) throws RemoteException;
- public ContentProviderResult[] applyBatch(String callingPkg, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
+ String mode, ICancellationSignal signal, IBinder callerToken)
+ throws RemoteException, FileNotFoundException;
+
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException;
+
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#call(java.lang.String, java.lang.String, android.os.Bundle)} "
+ + "instead")
public default Bundle call(String callingPkg, String method,
@Nullable String arg, @Nullable Bundle extras) throws RemoteException {
- return call(callingPkg, "unknown", method, arg, extras);
+ return call(callingPkg, null, "unknown", method, arg, extras);
}
- public Bundle call(String callingPkg, String authority, String method,
- @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+ public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags)
- throws RemoteException;
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
+ int modeFlags) throws RemoteException;
public ICancellationSignal createCancellationSignal() throws RemoteException;
- public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException;
- public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException;
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException;
- public boolean refresh(String callingPkg, Uri url, @Nullable Bundle args,
- ICancellationSignal cancellationSignal) throws RemoteException;
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException;
+
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ @Nullable Bundle args, ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException;
/* IPC constants */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index d4abf28..9d14d9d 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -151,6 +151,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static int getInt(@NonNull String key, int def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_int(key, def);
@@ -166,6 +167,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static long getLong(@NonNull String key, long def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_long(key, def);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b7a3c8f..3476b18 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1909,7 +1909,14 @@
* Returns the user-wide restrictions imposed on the user specified by <code>userHandle</code>.
* @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
* @return a Bundle containing all the restrictions.
+ *
+ * <p>Requires {@code android.permission.MANAGE_USERS} or
+ * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+ * must be the calling user or a managed profile associated with it.
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public Bundle getUserRestrictions(UserHandle userHandle) {
try {
return mService.getUserRestrictions(userHandle.getIdentifier());
@@ -2000,7 +2007,7 @@
* @return {@code true} if the current user has the given restriction, {@code false} otherwise.
*/
public boolean hasUserRestriction(String restrictionKey) {
- return hasUserRestriction(restrictionKey, Process.myUserHandle());
+ return hasUserRestrictionForUser(restrictionKey, Process.myUserHandle());
}
/**
@@ -2012,9 +2019,29 @@
*/
@UnsupportedAppUsage
public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
+ return hasUserRestrictionForUser(restrictionKey, userHandle);
+ }
+
+ /**
+ * Returns whether the given user has been disallowed from performing certain actions
+ * or setting certain settings.
+ * @param restrictionKey the string key representing the restriction
+ * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+ *
+ * <p>Requires {@code android.permission.MANAGE_USERS} or
+ * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+ * must be the calling user or a managed profile associated with it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ public boolean hasUserRestrictionForUser(@NonNull String restrictionKey,
+ @NonNull UserHandle userHandle) {
try {
- return mService.hasUserRestriction(restrictionKey,
- userHandle.getIdentifier());
+ return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 8b8afd5..bb8b041 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -586,9 +586,8 @@
@RequiresPermission(WRITE_DEVICE_CONFIG)
public static boolean setProperty(@NonNull String namespace, @NonNull String name,
@Nullable String value, boolean makeDefault) {
- String compositeName = createCompositeName(namespace, name);
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
+ return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
}
/**
@@ -672,12 +671,6 @@
}
}
- private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
- Preconditions.checkNotNull(namespace);
- Preconditions.checkNotNull(name);
- return namespace + "/" + name;
- }
-
private static Uri createNamespaceUri(@NonNull String namespace) {
Preconditions.checkNotNull(namespace);
return CONTENT_URI.buildUpon().appendPath(namespace).build();
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 2143a0d..a80153d 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1081,7 +1081,8 @@
// signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
// MANAGE_DOCUMENTS or associated URI permission here instead
final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
- enforceWritePermissionInner(rootUri, getCallingPackage(), null);
+ enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String rootId = DocumentsContract.getRootId(rootUri);
ejectRoot(rootId);
@@ -1102,7 +1103,8 @@
enforceTree(documentUri);
if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String childAuthority = childUri.getAuthority();
@@ -1114,7 +1116,8 @@
&& isChildDocument(documentId, childId));
} else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -1128,7 +1131,8 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
final IntentSender intentSender = createWebLinkIntent(documentId, options);
@@ -1136,7 +1140,8 @@
out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -1160,7 +1165,8 @@
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
deleteDocument(documentId);
// Document no longer exists, clean up any grants.
@@ -1170,8 +1176,10 @@
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String newDocumentId = copyDocument(documentId, targetId);
@@ -1194,9 +1202,12 @@
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
@@ -1217,8 +1228,10 @@
final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
removeDocument(documentId, parentSourceId);
// It's responsibility of the provider to revoke any grants, as the document may be
@@ -1227,7 +1240,8 @@
final boolean isTreeUri = isTreeUri(documentUri);
if (isTreeUri) {
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
} else {
getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
}
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 8f772d4..fcbda5f 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -334,10 +334,17 @@
return cachedTypeface;
}
- // Unfortunately the typeface is not available at this time, but requesting from the font
- // provider takes too much time. For now, request the font data to ensure it is in the cache
- // next time and return.
synchronized (sLock) {
+ // It is possible that Font is loaded during the thread sleep time
+ // re-check the cache to avoid re-loading the font
+ cachedTypeface = sTypefaceCache.get(id);
+ if (cachedTypeface != null) {
+ return cachedTypeface;
+ }
+
+ // Unfortunately the typeface is not available at this time, but requesting from
+ // the font provider takes too much time. For now, request the font data to ensure
+ // it is in the cache next time and return.
if (sHandler == null) {
sThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND);
sThread.start();
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index a1333df..aa67d97 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -47,6 +47,7 @@
import android.media.ExifInterface;
import android.media.MediaFile;
import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -589,9 +590,7 @@
* @see MediaStore#setIncludeTrashed(Uri)
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
- * @removed
*/
- @Deprecated
public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
}
@@ -830,9 +829,7 @@
* @see MediaStore#setIncludeTrashed(Uri)
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
- * @removed
*/
- @Deprecated
public static void trash(@NonNull Context context, @NonNull Uri uri) {
trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
}
@@ -850,9 +847,7 @@
* @see MediaStore#setIncludeTrashed(Uri)
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
- * @removed
*/
- @Deprecated
public static void trash(@NonNull Context context, @NonNull Uri uri,
@DurationMillisLong long timeoutMillis) {
if (timeoutMillis < 0) {
@@ -874,9 +869,7 @@
* @see MediaStore#setIncludeTrashed(Uri)
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
- * @removed
*/
- @Deprecated
public static void untrash(@NonNull Context context, @NonNull Uri uri) {
final ContentValues values = new ContentValues();
values.put(MediaColumns.IS_TRASHED, 0);
@@ -907,26 +900,8 @@
public static final String DATA = "_data";
/**
- * Hash of the media item on disk.
- * <p>
- * Contains a 20-byte binary blob which is the SHA-1 hash of the file as
- * persisted on disk. For performance reasons, the hash may not be
- * immediately available, in which case a {@code NULL} value will be
- * returned. If the underlying file is modified, this value will be
- * cleared and recalculated.
- * <p>
- * If you require the hash of a specific item, you can call
- * {@link ContentResolver#canonicalize(Uri)}, which will block until the
- * hash is calculated.
- *
- * @removed
- */
- @Deprecated
- @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true)
- public static final String HASH = "_hash";
-
- /**
- * The size of the media item.
+ * Indexed value of {@link File#length()} extracted from this media
+ * item.
*/
@BytesLong
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -943,12 +918,6 @@
public static final String DISPLAY_NAME = "_display_name";
/**
- * The title of the media item.
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
- public static final String TITLE = "title";
-
- /**
* The time the media item was first added.
*/
@CurrentTimeSecondsLong
@@ -956,14 +925,22 @@
public static final String DATE_ADDED = "date_added";
/**
- * The time the media item was last modified.
+ * Indexed value of {@link File#lastModified()} extracted from this
+ * media item.
*/
@CurrentTimeSecondsLong
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String DATE_MODIFIED = "date_modified";
/**
- * The time the media item was taken.
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DATE} or
+ * {@link ExifInterface#TAG_DATETIME_ORIGINAL} extracted from this media
+ * item.
+ * <p>
+ * Note that images must define both
+ * {@link ExifInterface#TAG_DATETIME_ORIGINAL} and
+ * {@code ExifInterface#TAG_OFFSET_TIME_ORIGINAL} to reliably determine
+ * this value in relation to the epoch.
*/
@CurrentTimeMillisLong
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -989,17 +966,6 @@
public static final String MIME_TYPE = "mime_type";
/**
- * The MTP object handle of a newly transfered file.
- * Used to pass the new file's object handle through the media scanner
- * from MTP to the media provider
- * For internal use only by MTP, media scanner and media provider.
- * @hide
- */
- @Deprecated
- // @Column(Cursor.FIELD_TYPE_INTEGER)
- public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
-
- /**
* Non-zero if the media file is drm-protected
* @hide
*/
@@ -1012,6 +978,10 @@
* Flag indicating if a media item is pending, and still being inserted
* by its owner. While this flag is set, only the owner of the item can
* open the underlying file; requests from other apps will be rejected.
+ * <p>
+ * Pending items are retained either until they are published by setting
+ * the field to {@code 0}, or until they expire as defined by
+ * {@link #DATE_EXPIRES}.
*
* @see MediaStore#setIncludePending(Uri)
*/
@@ -1020,38 +990,54 @@
/**
* Flag indicating if a media item is trashed.
+ * <p>
+ * Trashed items are retained until they expire as defined by
+ * {@link #DATE_EXPIRES}.
*
* @see MediaColumns#IS_TRASHED
* @see MediaStore#setIncludeTrashed(Uri)
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
- * @removed
*/
- @Deprecated
@Column(Cursor.FIELD_TYPE_INTEGER)
public static final String IS_TRASHED = "is_trashed";
/**
* The time the media item should be considered expired. Typically only
- * meaningful in the context of {@link #IS_PENDING}.
+ * meaningful in the context of {@link #IS_PENDING} or
+ * {@link #IS_TRASHED}.
*/
@CurrentTimeSecondsLong
@Column(Cursor.FIELD_TYPE_INTEGER)
public static final String DATE_EXPIRES = "date_expires";
/**
- * The width of the media item, in pixels.
+ * Indexed value of
+ * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_WIDTH},
+ * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_WIDTH} or
+ * {@link ExifInterface#TAG_IMAGE_WIDTH} extracted from this media item.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String WIDTH = "width";
/**
- * The height of the media item, in pixels.
+ * Indexed value of
+ * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_HEIGHT},
+ * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_HEIGHT} or
+ * {@link ExifInterface#TAG_IMAGE_LENGTH} extracted from this media
+ * item.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String HEIGHT = "height";
/**
+ * Calculated value that combines {@link #WIDTH} and {@link #HEIGHT}
+ * into a user-presentable string.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String RESOLUTION = "resolution";
+
+ /**
* Package name that contributed this media. The value may be
* {@code NULL} if ownership cannot be reliably determined.
*/
@@ -1097,28 +1083,6 @@
public static final String RELATIVE_PATH = "relative_path";
/**
- * The primary directory name this media exists under. The value may be
- * {@code NULL} if the media doesn't have a primary directory name.
- *
- * @removed
- * @deprecated Replaced by {@link #RELATIVE_PATH}.
- */
- @Column(Cursor.FIELD_TYPE_STRING)
- @Deprecated
- public static final String PRIMARY_DIRECTORY = "primary_directory";
-
- /**
- * The secondary directory name this media exists under. The value may
- * be {@code NULL} if the media doesn't have a secondary directory name.
- *
- * @removed
- * @deprecated Replaced by {@link #RELATIVE_PATH}.
- */
- @Column(Cursor.FIELD_TYPE_STRING)
- @Deprecated
- public static final String SECONDARY_DIRECTORY = "secondary_directory";
-
- /**
* The primary bucket ID of this media item. This can be useful to
* present the user a first-level clustering of related media items.
* This is a read-only column that is automatically computed.
@@ -1191,18 +1155,171 @@
public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
/**
- * The duration of the media item.
+ * Indexed value of
+ * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_ROTATION},
+ * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_ROTATION}, or
+ * {@link ExifInterface#TAG_ORIENTATION} extracted from this media item.
+ * <p>
+ * For consistency the indexed value is expressed in degrees, such as 0,
+ * 90, 180, or 270.
+ */
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String ORIENTATION = "orientation";
+
+ /**
+ * Flag indicating if the media item has been marked as being a
+ * "favorite" by the user.
+ */
+ @Column(Cursor.FIELD_TYPE_INTEGER)
+ public static final String IS_FAVORITE = "is_favorite";
+
+ // =======================================
+ // ==== MediaMetadataRetriever values ====
+ // =======================================
+
+ /**
+ * Indexed value of
+ * {@link MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER} extracted
+ * from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String CD_TRACK_NUMBER = "cd_track_number";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ALBUM}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String ALBUM = "album";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ARTIST}
+ * or {@link ExifInterface#TAG_ARTIST} extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String ARTIST = "artist";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_AUTHOR}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String AUTHOR = "author";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_COMPOSER}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String COMPOSER = "composer";
+
+ // METADATA_KEY_DATE is DATE_TAKEN
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_GENRE}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String GENRE = "genre";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_TITLE}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String TITLE = "title";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_YEAR}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String YEAR = "year";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DURATION}
+ * extracted from this media item.
*/
@DurationMillisLong
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String DURATION = "duration";
/**
- * The orientation for the media item, expressed in degrees. For
- * example, 0, 90, 180, or 270 degrees.
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_NUM_TRACKS}
+ * extracted from this media item.
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
- public static final String ORIENTATION = "orientation";
+ public static final String NUM_TRACKS = "num_tracks";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_WRITER}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String WRITER = "writer";
+
+ // METADATA_KEY_MIMETYPE is MIME_TYPE
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String ALBUM_ARTIST = "album_artist";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String DISC_NUMBER = "disc_number";
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_COMPILATION}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String COMPILATION = "compilation";
+
+ // HAS_AUDIO is ignored
+ // HAS_VIDEO is ignored
+ // VIDEO_WIDTH is WIDTH
+ // VIDEO_HEIGHT is HEIGHT
+
+ /**
+ * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_BITRATE}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String BITRATE = "bitrate";
+
+ // TIMED_TEXT_LANGUAGES is ignored
+ // IS_DRM is ignored
+ // LOCATION is LATITUDE and LONGITUDE
+ // VIDEO_ROTATION is ORIENTATION
+
+ /**
+ * Indexed value of
+ * {@link MediaMetadataRetriever#METADATA_KEY_CAPTURE_FRAMERATE}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
+ public static final String CAPTURE_FRAMERATE = "capture_framerate";
+
+ // HAS_IMAGE is ignored
+ // IMAGE_COUNT is ignored
+ // IMAGE_PRIMARY is ignored
+ // IMAGE_WIDTH is WIDTH
+ // IMAGE_HEIGHT is HEIGHT
+ // IMAGE_ROTATION is ORIENTATION
+ // VIDEO_FRAME_COUNT is ignored
+ // EXIF_OFFSET is ignored
+ // EXIF_LENGTH is ignored
+ // COLOR_STANDARD is ignored
+ // COLOR_TRANSFER is ignored
+ // COLOR_RANGE is ignored
+ // SAMPLERATE is ignored
+ // BITS_PER_SAMPLE is ignored
}
/**
@@ -1331,10 +1448,7 @@
@Column(Cursor.FIELD_TYPE_STRING)
public static final String MIME_TYPE = "mime_type";
- /**
- * The title of the media item.
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String TITLE = "title";
/**
@@ -1589,12 +1703,6 @@
*/
public interface ImageColumns extends MediaColumns {
/**
- * The description of the image
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
- public static final String DESCRIPTION = "description";
-
- /**
* The picasa id of the image
*
* @deprecated this value was only relevant for images hosted on
@@ -1656,6 +1764,34 @@
public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
/** @removed promoted to parent interface */
public static final String GROUP_ID = "group_id";
+
+ /**
+ * Indexed value of {@link ExifInterface#TAG_IMAGE_DESCRIPTION}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String DESCRIPTION = "description";
+
+ /**
+ * Indexed value of {@link ExifInterface#TAG_EXPOSURE_TIME}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String EXPOSURE_TIME = "exposure_time";
+
+ /**
+ * Indexed value of {@link ExifInterface#TAG_F_NUMBER}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String F_NUMBER = "f_number";
+
+ /**
+ * Indexed value of {@link ExifInterface#TAG_ISO_SPEED_RATINGS}
+ * extracted from this media item.
+ */
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String ISO = "iso";
}
public static final class Media implements ImageColumns {
@@ -1904,6 +2040,10 @@
* generated. Callers are responsible for their own in-memory
* caching of returned values.
*
+ * As of {@link android.os.Build.VERSION_CODES#Q}, this output
+ * of the thumbnail has correct rotation, don't need to rotate
+ * it again.
+ *
* @param imageId the image item to obtain a thumbnail for.
* @param kind optimal thumbnail size desired.
* @return decoded thumbnail, or {@code null} if problem was
@@ -1946,6 +2086,10 @@
* generated. Callers are responsible for their own in-memory
* caching of returned values.
*
+ * As of {@link android.os.Build.VERSION_CODES#Q}, this output
+ * of the thumbnail has correct rotation, don't need to rotate
+ * it again.
+ *
* @param imageId the image item to obtain a thumbnail for.
* @param kind optimal thumbnail size desired.
* @return decoded thumbnail, or {@code null} if problem was
@@ -2000,6 +2144,9 @@
* {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
* access.
*
+ * As of {@link android.os.Build.VERSION_CODES#Q}, this thumbnail
+ * has correct rotation, don't need to rotate it again.
+ *
* @deprecated Apps may not have filesystem permissions to directly
* access this path. Instead of trying to open this path
* directly, apps should use
@@ -2093,10 +2240,7 @@
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String ARTIST_ID = "artist_id";
- /**
- * The artist who created the audio file, if any
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String ARTIST = "artist";
/**
@@ -2107,14 +2251,6 @@
public static final String ALBUM_ARTIST = "album_artist";
/**
- * Whether the song is part of a compilation
- * @hide
- */
- @Deprecated
- // @Column(Cursor.FIELD_TYPE_STRING)
- public static final String COMPILATION = "compilation";
-
- /**
* A non human readable key calculated from the ARTIST, used for
* searching, sorting and grouping
*
@@ -2131,10 +2267,7 @@
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ARTIST_KEY = "artist_key";
- /**
- * The composer of the audio file, if any
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String COMPOSER = "composer";
/**
@@ -2143,10 +2276,7 @@
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String ALBUM_ID = "album_id";
- /**
- * The album the audio file is from, if any
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String ALBUM = "album";
/**
@@ -2973,23 +3103,11 @@
public interface VideoColumns extends MediaColumns {
/** @removed promoted to parent interface */
public static final String DURATION = "duration";
-
- /**
- * The artist who created the video file, if any
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String ARTIST = "artist";
-
- /**
- * The album the video file is from, if any
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String ALBUM = "album";
-
- /**
- * The resolution of the video file, formatted as "XxY"
- */
- @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ /** @removed promoted to parent interface */
public static final String RESOLUTION = "resolution";
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b5580d4..44ab09e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -78,6 +78,7 @@
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.ILockSettings;
import java.io.IOException;
@@ -2306,8 +2307,8 @@
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallSetCommand, name, arg);
+ cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -2380,14 +2381,15 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallGetCommand, name, args);
+ b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
+ args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallGetCommand, name, args);
+ b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
}
if (b != null) {
String value = b.getString(Settings.NameValueTable.VALUE);
@@ -2455,14 +2457,14 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs,
- null);
+ c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+ SELECT_VALUE_PROJECTION, queryArgs, null);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs,
- null);
+ c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+ SELECT_VALUE_PROJECTION, queryArgs, null);
}
if (c == null) {
Log.w(TAG, "Can't get key " + name + " from " + mUri);
@@ -2506,7 +2508,7 @@
boolean prefixCached = false;
int size = mValues.size();
for (int i = 0; i < size; ++i) {
- if (mValues.keyAt(i).startsWith(prefix + "/")) {
+ if (mValues.keyAt(i).startsWith(prefix)) {
prefixCached = true;
break;
}
@@ -2521,7 +2523,7 @@
} else {
for (int i = 0; i < size; ++i) {
String key = mValues.keyAt(i);
- if (key.startsWith(prefix + "/")) {
+ if (key.startsWith(prefix)) {
keyValues.put(key, mValues.get(key));
}
}
@@ -2557,8 +2559,8 @@
}
// Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallListCommand, null, args);
+ Bundle b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
if (b == null) {
// Invalid response, return an empty map
return keyValues;
@@ -5132,8 +5134,8 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
- CALL_METHOD_RESET_SECURE, null, arg);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
}
@@ -8268,6 +8270,12 @@
public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled";
/**
+ * Controls whether tap gesture is enabled.
+ * @hide
+ */
+ public static final String TAP_GESTURE = "tap_gesture";
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -12821,8 +12829,8 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
- CALL_METHOD_RESET_GLOBAL, null, arg);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
}
@@ -13678,10 +13686,10 @@
}
/**
- * Look up a list of names in the database, based on a common prefix.
+ * Look up a list of names in the database, within the specified namespace.
*
* @param resolver to access the database with
- * @param prefix to apply to all of the names which will be fetched
+ * @param namespace to which the names belong
* @param names to look up in the table
* @return a non null, but possibly empty, map from name to value for any of the names that
* were found during lookup.
@@ -13690,16 +13698,17 @@
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
static Map<String, String> getStrings(@NonNull ContentResolver resolver,
- @NonNull String prefix, @NonNull List<String> names) {
- List<String> concatenatedNames = new ArrayList<>(names.size());
+ @NonNull String namespace, @NonNull List<String> names) {
+ List<String> compositeNames = new ArrayList<>(names.size());
for (String name : names) {
- concatenatedNames.add(prefix + "/" + name);
+ compositeNames.add(createCompositeName(namespace, name));
}
+ String prefix = createPrefix(namespace);
ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
- resolver, prefix, concatenatedNames);
+ resolver, prefix, compositeNames);
int size = rawKeyValues.size();
- int substringLength = prefix.length() + 1;
+ int substringLength = prefix.length();
ArrayMap<String, String> keyValues = new ArrayMap<>(size);
for (int i = 0; i < size; ++i) {
keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
@@ -13709,7 +13718,7 @@
}
/**
- * Store a name/value pair into the database.
+ * Store a name/value pair into the database within the specified namespace.
* <p>
* Also the method takes an argument whether to make the value the default for this setting.
* If the system already specified a default value, then the one passed in here will
@@ -13717,6 +13726,7 @@
* </p>
*
* @param resolver to access the database with.
+ * @param namespace to store the name/value pair in.
* @param name to store.
* @param value to associate with the name.
* @param makeDefault whether to make the value the default one.
@@ -13727,10 +13737,10 @@
* @hide
*/
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static boolean putString(@NonNull ContentResolver resolver, @NonNull String name,
- @Nullable String value, boolean makeDefault) {
- return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault,
- resolver.getUserId());
+ static boolean putString(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull String name, @Nullable String value, boolean makeDefault) {
+ return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name),
+ value, null, makeDefault, resolver.getUserId());
}
/**
@@ -13741,29 +13751,40 @@
*
* @param resolver Handle to the content resolver.
* @param resetMode The reset mode to use.
- * @param prefix Optionally, to limit which which pairs are reset.
+ * @param namespace Optionally, to limit which which namespace is reset.
*
- * @see #putString(ContentResolver, String, String, boolean)
+ * @see #putString(ContentResolver, String, String, String, boolean)
*
* @hide
*/
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
- @Nullable String prefix) {
+ @Nullable String namespace) {
try {
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
- if (prefix != null) {
- arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+ if (namespace != null) {
+ arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
}
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
- CALL_METHOD_RESET_CONFIG, null, arg);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
}
}
+
+ private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
+ Preconditions.checkNotNull(namespace);
+ Preconditions.checkNotNull(name);
+ return createPrefix(namespace) + name;
+ }
+
+ private static String createPrefix(@NonNull String namespace) {
+ Preconditions.checkNotNull(namespace);
+ return namespace + "/";
+ }
}
/**
diff --git a/core/java/android/provider/SettingsStringUtil.java b/core/java/android/provider/SettingsStringUtil.java
index a3dc947..9e495dd 100644
--- a/core/java/android/provider/SettingsStringUtil.java
+++ b/core/java/android/provider/SettingsStringUtil.java
@@ -126,7 +126,7 @@
@Override
protected String itemToString(ComponentName item) {
- return item.flattenToString();
+ return item != null ? item.flattenToString() : "null";
}
public static String add(String delimitedElements, ComponentName element) {
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
new file mode 100644
index 0000000..de90b94
--- /dev/null
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carrier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Provides basic structure for platform to connect to the carrier messaging service.
+ * <p>
+ * <code>
+ * CarrierMessagingServiceWrapper carrierMessagingServiceWrapper =
+ * new CarrierMessagingServiceWrapperImpl();
+ * if (carrierMessagingServiceWrapper.bindToCarrierMessagingService(context, carrierPackageName)) {
+ * // wait for onServiceReady callback
+ * } else {
+ * // Unable to bind: handle error.
+ * }
+ * </code>
+ * <p> Upon completion {@link #disposeConnection} should be called to unbind the
+ * CarrierMessagingService.
+ * @hide
+ */
+@SystemApi
+public abstract class CarrierMessagingServiceWrapper {
+ // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
+ // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
+ private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
+
+ private volatile ICarrierMessagingService mICarrierMessagingService;
+
+ /**
+ * Binds to the carrier messaging service under package {@code carrierPackageName}. This method
+ * should be called exactly once.
+ *
+ * @param context the context
+ * @param carrierPackageName the carrier package name
+ * @return true upon successfully binding to a carrier messaging service, false otherwise
+ * @hide
+ */
+ @SystemApi
+ public boolean bindToCarrierMessagingService(@NonNull Context context,
+ @NonNull String carrierPackageName) {
+ Preconditions.checkState(mCarrierMessagingServiceConnection == null);
+
+ Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
+ intent.setPackage(carrierPackageName);
+ mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
+ return context.bindService(intent, mCarrierMessagingServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Unbinds the carrier messaging service. This method should be called exactly once.
+ *
+ * @param context the context
+ * @hide
+ */
+ @SystemApi
+ public void disposeConnection(@NonNull Context context) {
+ Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
+ context.unbindService(mCarrierMessagingServiceConnection);
+ mCarrierMessagingServiceConnection = null;
+ }
+
+ /**
+ * Implemented by subclasses to use the carrier messaging service once it is ready.
+ * @hide
+ */
+ @SystemApi
+ public abstract void onServiceReady();
+
+ /**
+ * Called when connection with service is established.
+ *
+ * @param carrierMessagingService the carrier messaing service interface
+ */
+ private void onServiceReady(ICarrierMessagingService carrierMessagingService) {
+ mICarrierMessagingService = carrierMessagingService;
+ onServiceReady();
+ }
+
+ /**
+ * Request filtering an incoming SMS message.
+ * The service will call callback.onFilterComplete with the filtering result.
+ *
+ * @param pdu the PDUs of the message
+ * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
+ * @param destPort the destination port of a data SMS. It will be -1 for text SMS
+ * @param subId SMS subscription ID of the SIM
+ * @param callback the callback to notify upon completion
+ * @hide
+ */
+ @SystemApi
+ public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
+ int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
+ if (mICarrierMessagingService != null) {
+ try {
+ mICarrierMessagingService.filterSms(pdu, format, destPort, subId,
+ new CarrierMessagingCallbackWrapperInternal(callback));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Request sending a new text SMS from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send
+ * status.
+ *
+ * @param text the text to send
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
+ * @param callback the callback to notify upon completion
+ * @hide
+ */
+ @SystemApi
+ public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
+ int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
+ if (mICarrierMessagingService != null) {
+ try {
+ mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag,
+ new CarrierMessagingCallbackWrapperInternal(callback));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Request sending a new data SMS from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send
+ * status.
+ *
+ * @param data the data to send
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param destPort port number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
+ * @param callback the callback to notify upon completion
+ * @hide
+ */
+ @SystemApi
+ public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
+ int destPort, int sendSmsFlag,
+ @NonNull final CarrierMessagingCallbackWrapper callback) {
+ if (mICarrierMessagingService != null) {
+ try {
+ mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort,
+ sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Request sending a new multi-part text SMS from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendMultipartSmsComplete}
+ * with the send status.
+ *
+ * @param parts the parts of the multi-part text SMS to send
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
+ * @param callback the callback to notify upon completion
+ * @hide
+ */
+ @SystemApi
+ public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
+ @NonNull String destAddress, int sendSmsFlag,
+ @NonNull final CarrierMessagingCallbackWrapper callback) {
+ if (mICarrierMessagingService != null) {
+ try {
+ mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress,
+ sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Request sending a new MMS PDU from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendMmsComplete} with the send
+ * status.
+ *
+ * @param pduUri the content provider URI of the PDU to send
+ * @param subId SMS subscription ID of the SIM
+ * @param location the optional URI to send this MMS PDU. If this is {code null},
+ * the PDU should be sent to the default MMSC URL.
+ * @param callback the callback to notify upon completion
+ * @hide
+ */
+ @SystemApi
+ public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
+ @NonNull final CarrierMessagingCallbackWrapper callback) {
+ if (mICarrierMessagingService != null) {
+ try {
+ mICarrierMessagingService.sendMms(pduUri, subId, location,
+ new CarrierMessagingCallbackWrapperInternal(callback));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Request downloading a new MMS.
+ * The service will call {@link ICarrierMessagingCallback#onDownloadMmsComplete} with the
+ * download status.
+ *
+ * @param pduUri the content provider URI of the PDU to be downloaded.
+ * @param subId SMS subscription ID of the SIM
+ * @param location the URI of the message to be downloaded.
+ * @param callback the callback to notify upon completion
+ * @hide
+ */
+ @SystemApi
+ public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
+ @NonNull final CarrierMessagingCallbackWrapper callback) {
+ if (mICarrierMessagingService != null) {
+ try {
+ mICarrierMessagingService.downloadMms(pduUri, subId, location,
+ new CarrierMessagingCallbackWrapperInternal(callback));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * A basic {@link ServiceConnection}.
+ */
+ private final class CarrierMessagingServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }
+
+ /**
+ * Callback wrapper used for response to requests exposed by
+ * {@link CarrierMessagingServiceWrapper}.
+ * @hide
+ */
+ @SystemApi
+ public abstract static class CarrierMessagingCallbackWrapper {
+
+ /**
+ * Response callback for {@link CarrierMessagingServiceWrapper#filterSms}.
+ * @param result a bitmask integer to indicate how the incoming text SMS should be handled
+ * by the platform. Bits set can be
+ * {@link CarrierMessagingService#RECEIVE_OPTIONS_DROP} and
+ * {@link CarrierMessagingService#
+ * RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE}.
+ * {@see CarrierMessagingService#onReceiveTextSms}.
+ * @hide
+ */
+ @SystemApi
+ public void onFilterComplete(int result) {
+
+ }
+
+ /**
+ * Response callback for {@link CarrierMessagingServiceWrapper#sendTextSms} and
+ * {@link CarrierMessagingServiceWrapper#sendDataSms}.
+ * @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
+ * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
+ * and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
+ * @param messageRef message reference of the just-sent message. This field is applicable
+ * only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
+ * @hide
+ */
+ @SystemApi
+ public void onSendSmsComplete(int result, int messageRef) {
+
+ }
+
+ /**
+ * Response callback for {@link CarrierMessagingServiceWrapper#sendMultipartTextSms}.
+ * @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
+ * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
+ * and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
+ * @param messageRefs an array of message references, one for each part of the
+ * multipart SMS. This field is applicable only if result is
+ * {@link CarrierMessagingService#SEND_STATUS_OK}.
+ * @hide
+ */
+ @SystemApi
+ public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
+
+ }
+
+ /**
+ * Response callback for {@link CarrierMessagingServiceWrapper#sendMms}.
+ * @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
+ * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
+ * and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
+ * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message
+ * was sent. sendConfPdu is ignored if the {@code result} is not
+ * {@link CarrierMessagingService#SEND_STATUS_OK}.
+ * @hide
+ */
+ @SystemApi
+ public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
+
+ }
+
+ /**
+ * Response callback for {@link CarrierMessagingServiceWrapper#downloadMms}.
+ * @param result download status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
+ * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
+ * and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
+ * @hide
+ */
+ @SystemApi
+ public void onDownloadMmsComplete(int result) {
+
+ }
+ }
+
+ private final class CarrierMessagingCallbackWrapperInternal
+ extends ICarrierMessagingCallback.Stub {
+ CarrierMessagingCallbackWrapper mCarrierMessagingCallbackWrapper;
+
+ CarrierMessagingCallbackWrapperInternal(CarrierMessagingCallbackWrapper callback) {
+ mCarrierMessagingCallbackWrapper = callback;
+ }
+
+ @Override
+ public void onFilterComplete(int result) throws RemoteException {
+ mCarrierMessagingCallbackWrapper.onFilterComplete(result);
+ }
+
+ @Override
+ public void onSendSmsComplete(int result, int messageRef) throws RemoteException {
+ mCarrierMessagingCallbackWrapper.onSendSmsComplete(result, messageRef);
+ }
+
+ @Override
+ public void onSendMultipartSmsComplete(int result, int[] messageRefs)
+ throws RemoteException {
+ mCarrierMessagingCallbackWrapper.onSendMultipartSmsComplete(result, messageRefs);
+ }
+
+ @Override
+ public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException {
+ mCarrierMessagingCallbackWrapper.onSendMmsComplete(result, sendConfPdu);
+ }
+
+ @Override
+ public void onDownloadMmsComplete(int result) throws RemoteException {
+ mCarrierMessagingCallbackWrapper.onDownloadMmsComplete(result);
+ }
+ }
+}
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
index 91a5ec0..8d9607f 100644
--- a/core/java/android/util/StatsEvent.java
+++ b/core/java/android/util/StatsEvent.java
@@ -20,312 +20,540 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
/**
* StatsEvent builds and stores the buffer sent over the statsd socket.
* This class defines and encapsulates the socket protocol.
+ *
+ * <p>Usage:</p>
+ * <pre>
+ * StatsEvent statsEvent = StatsEvent.newBuilder()
+ * .setAtomId(atomId)
+ * .writeBoolean(false)
+ * .writeString("annotated String field")
+ * .addBooleanAnnotation(annotationId, true)
+ * .build();
+ *
+ * StatsLog.write(statsEvent);
+ * </pre>
* @hide
**/
-public final class StatsEvent implements AutoCloseable {
- private static final int POS_NUM_ELEMENTS = 1;
- private static final int POS_TIMESTAMP = POS_NUM_ELEMENTS + 1;
-
+public final class StatsEvent {
private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;
- // Max payload size is 4 KB less 4 bytes which are reserved for statsEventTag.
+ // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
// See android_util_StatsLog.cpp.
- private static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+ private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
- private static final byte INT_TYPE = 0;
- private static final byte LONG_TYPE = 1;
- private static final byte STRING_TYPE = 2;
- private static final byte LIST_TYPE = 3;
- private static final byte FLOAT_TYPE = 4;
+ private final Buffer mBuffer;
+ private final int mNumBytes;
- private static final int INT_TYPE_SIZE = 5;
- private static final int FLOAT_TYPE_SIZE = 5;
- private static final int LONG_TYPE_SIZE = 9;
-
- private static final int STRING_TYPE_OVERHEAD = 5;
- private static final int LIST_TYPE_OVERHEAD = 2;
-
- public static final int SUCCESS = 0;
- public static final int ERROR_BUFFER_LIMIT_EXCEEDED = -1;
- public static final int ERROR_NO_TIMESTAMP = -2;
- public static final int ERROR_TIMESTAMP_ALREADY_WRITTEN = -3;
- public static final int ERROR_NO_ATOM_ID = -4;
- public static final int ERROR_ATOM_ID_ALREADY_WRITTEN = -5;
- public static final int ERROR_UID_TAG_COUNT_MISMATCH = -6;
-
- private static Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static StatsEvent sPool;
-
- private final byte[] mBuffer = new byte[MAX_EVENT_PAYLOAD];
- private int mPos;
- private int mNumElements;
- private int mAtomId;
-
- private StatsEvent() {
- // Write LIST_TYPE to buffer
- mBuffer[0] = LIST_TYPE;
- reset();
- }
-
- private void reset() {
- // Reset state.
- mPos = POS_TIMESTAMP;
- mNumElements = 0;
- mAtomId = 0;
+ private StatsEvent(@NonNull final Buffer buffer, final int numBytes) {
+ mBuffer = buffer;
+ mNumBytes = numBytes;
}
/**
- * Returns a StatsEvent object from the pool.
+ * Returns a new StatsEvent.Builder for building StatsEvent object.
**/
@NonNull
- public static StatsEvent obtain() {
- final StatsEvent statsEvent;
- synchronized (sLock) {
- statsEvent = null == sPool ? new StatsEvent() : sPool;
- sPool = null;
- }
- statsEvent.reset();
- return statsEvent;
+ public StatsEvent.Builder newBuilder() {
+ return new StatsEvent.Builder(Buffer.obtain());
}
- @Override
- public void close() {
- synchronized (sLock) {
- if (null == sPool) {
- sPool = this;
- }
- }
+ @NonNull
+ byte[] getBytes() {
+ return mBuffer.getBytes();
+ }
+
+ int getNumBytes() {
+ return mNumBytes;
+ }
+
+ void release() {
+ mBuffer.release();
}
/**
- * Writes the event timestamp to the buffer.
+ * Builder for constructing a StatsEvent object.
+ *
+ * <p>This class defines and encapsulates the socket encoding for the buffer.
+ * The write methods must be called in the same order as the order of fields in the
+ * atom definition.</p>
+ *
+ * <p>setAtomId() can be called anytime before build().</p>
+ *
+ * <p>Example:</p>
+ * <pre>
+ * // Atom definition.
+ * message MyAtom {
+ * optional int32 field1 = 1;
+ * optional int64 field2 = 2;
+ * optional string field3 = 3 [(annotation1) = true];
+ * }
+ *
+ * // StatsEvent construction.
+ * StatsEvent.newBuilder()
+ * StatsEvent statsEvent = StatsEvent.newBuilder()
+ * .setAtomId(atomId)
+ * .writeInt(3) // field1
+ * .writeLong(8L) // field2
+ * .writeString("foo") // field 3
+ * .addBooleanAnnotation(annotation1Id, true)
+ * .build();
+ * </pre>
+ * @hide
**/
- public int writeTimestampNs(final long timestampNs) {
- if (hasTimestamp()) {
- return ERROR_TIMESTAMP_ALREADY_WRITTEN;
- }
- return writeLong(timestampNs);
- }
+ public static final class Builder {
+ // Type Ids.
+ private static final byte TYPE_INT = 0x00;
+ private static final byte TYPE_LONG = 0x01;
+ private static final byte TYPE_STRING = 0x02;
+ private static final byte TYPE_LIST = 0x03;
+ private static final byte TYPE_FLOAT = 0x04;
+ private static final byte TYPE_BOOLEAN = 0x05;
+ private static final byte TYPE_OBJECT = 0x06;
+ private static final byte TYPE_BYTE_ARRAY = 0x07;
+ private static final byte TYPE_ATTRIBUTION_CHAIN = 0x08;
+ private static final byte TYPE_ERRORS = 0x0F;
- private boolean hasTimestamp() {
- return mPos > POS_TIMESTAMP;
- }
+ // Error flags.
+ private static final int ERROR_NO_TIMESTAMP = 0x1;
+ private static final int ERROR_NO_ATOM_ID = 0x2;
+ private static final int ERROR_OVERFLOW = 0x4;
+ private static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8;
+ private static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x10;
+ private static final int ERROR_INVALID_ANNOTATION_ID = 0x20;
+ private static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x40;
+ private static final int ERROR_TOO_MANY_ANNOTATIONS = 0x80;
+ private static final int ERROR_TOO_MANY_FIELDS = 0x100;
+ private static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x200;
- private boolean hasAtomId() {
- return mAtomId != 0;
- }
+ // Size limits.
+ private static final int MAX_ANNOTATION_COUNT = 15;
+ private static final int MAX_ATTRIBUTION_NODES = 127;
+ private static final int MAX_NUM_ELEMENTS = 127;
- /**
- * Writes the atom id to the buffer.
- **/
- public int writeAtomId(final int atomId) {
- if (!hasTimestamp()) {
- return ERROR_NO_TIMESTAMP;
- } else if (hasAtomId()) {
- return ERROR_ATOM_ID_ALREADY_WRITTEN;
+ // Fixed positions.
+ private static final int POS_NUM_ELEMENTS = 1;
+ private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES;
+ private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES;
+
+ private final Buffer mBuffer;
+ private long mTimestampNs;
+ private int mAtomId;
+ private byte mCurrentAnnotationCount;
+ private int mPos;
+ private int mPosLastField;
+ private byte mLastType;
+ private int mNumElements;
+ private int mErrorMask;
+
+ private Builder(final Buffer buffer) {
+ mBuffer = buffer;
+ mCurrentAnnotationCount = 0;
+ mAtomId = 0;
+ mTimestampNs = SystemClock.elapsedRealtimeNanos();
+ mNumElements = 0;
+
+ // Set mPos to 0 for writing TYPE_OBJECT at 0th position.
+ mPos = 0;
+ writeTypeId(TYPE_OBJECT);
+
+ // Set mPos to after atom id's location in the buffer.
+ // First 2 elements in the buffer are event timestamp followed by the atom id.
+ mPos = POS_ATOM_ID + Byte.BYTES + Integer.BYTES;
+ mPosLastField = 0;
+ mLastType = 0;
}
- final int writeResult = writeInt(atomId);
- if (SUCCESS == writeResult) {
+ /**
+ * Sets the atom id for this StatsEvent.
+ **/
+ @NonNull
+ public Builder setAtomId(final int atomId) {
mAtomId = atomId;
- }
- return writeResult;
- }
-
- /**
- * Appends the given int to the StatsEvent buffer.
- **/
- public int writeInt(final int value) {
- if (!hasTimestamp()) {
- return ERROR_NO_TIMESTAMP;
- } else if (!hasAtomId()) {
- return ERROR_NO_ATOM_ID;
- } else if (mPos + INT_TYPE_SIZE > MAX_EVENT_PAYLOAD) {
- return ERROR_BUFFER_LIMIT_EXCEEDED;
+ return this;
}
- mBuffer[mPos] = INT_TYPE;
- copyInt(mBuffer, mPos + 1, value);
- mPos += INT_TYPE_SIZE;
- mNumElements++;
- return SUCCESS;
- }
-
- /**
- * Appends the given long to the StatsEvent buffer.
- **/
- public int writeLong(final long value) {
- if (!hasTimestamp()) {
- return ERROR_NO_TIMESTAMP;
- } else if (!hasAtomId()) {
- return ERROR_NO_ATOM_ID;
- } else if (mPos + LONG_TYPE_SIZE > MAX_EVENT_PAYLOAD) {
- return ERROR_BUFFER_LIMIT_EXCEEDED;
+ /**
+ * Sets the timestamp in nanos for this StatsEvent.
+ **/
+ @VisibleForTesting
+ @NonNull
+ public Builder setTimestampNs(final long timestampNs) {
+ mTimestampNs = timestampNs;
+ return this;
}
- mBuffer[mPos] = LONG_TYPE;
- copyLong(mBuffer, mPos + 1, value);
- mPos += LONG_TYPE_SIZE;
- mNumElements++;
- return SUCCESS;
- }
-
- /**
- * Appends the given float to the StatsEvent buffer.
- **/
- public int writeFloat(final float value) {
- if (!hasTimestamp()) {
- return ERROR_NO_TIMESTAMP;
- } else if (!hasAtomId()) {
- return ERROR_NO_ATOM_ID;
- } else if (mPos + FLOAT_TYPE_SIZE > MAX_EVENT_PAYLOAD) {
- return ERROR_BUFFER_LIMIT_EXCEEDED;
+ /**
+ * Write a boolean field to this StatsEvent.
+ **/
+ @NonNull
+ public Builder writeBoolean(final boolean value) {
+ // Write boolean typeId byte followed by boolean byte representation.
+ writeTypeId(TYPE_BOOLEAN);
+ mPos += mBuffer.putBoolean(mPos, value);
+ mNumElements++;
+ return this;
}
- mBuffer[mPos] = FLOAT_TYPE;
- copyInt(mBuffer, mPos + 1, Float.floatToIntBits(value));
- mPos += FLOAT_TYPE_SIZE;
- mNumElements++;
- return SUCCESS;
- }
-
- /**
- * Appends the given boolean to the StatsEvent buffer.
- **/
- public int writeBoolean(final boolean value) {
- return writeInt(value ? 1 : 0);
- }
-
- /**
- * Appends the given byte array to the StatsEvent buffer.
- **/
- public int writeByteArray(@NonNull final byte[] value) {
- if (!hasTimestamp()) {
- return ERROR_NO_TIMESTAMP;
- } else if (!hasAtomId()) {
- return ERROR_NO_ATOM_ID;
- } else if (mPos + STRING_TYPE_OVERHEAD + value.length > MAX_EVENT_PAYLOAD) {
- return ERROR_BUFFER_LIMIT_EXCEEDED;
+ /**
+ * Write an integer field to this StatsEvent.
+ **/
+ @NonNull
+ public Builder writeInt(final int value) {
+ // Write integer typeId byte followed by 4-byte representation of value.
+ writeTypeId(TYPE_INT);
+ mPos += mBuffer.putInt(mPos, value);
+ mNumElements++;
+ return this;
}
- mBuffer[mPos] = STRING_TYPE;
- copyInt(mBuffer, mPos + 1, value.length);
- System.arraycopy(value, 0, mBuffer, mPos + STRING_TYPE_OVERHEAD, value.length);
- mPos += STRING_TYPE_OVERHEAD + value.length;
- mNumElements++;
- return SUCCESS;
- }
-
- /**
- * Appends the given String to the StatsEvent buffer.
- **/
- public int writeString(@NonNull final String value) {
- final byte[] valueBytes = stringToBytes(value);
- return writeByteArray(valueBytes);
- }
-
- /**
- * Appends the AttributionNode specified as array of uids and array of tags.
- **/
- public int writeAttributionNode(@NonNull final int[] uids, @NonNull final String[] tags) {
- if (!hasTimestamp()) {
- return ERROR_NO_TIMESTAMP;
- } else if (!hasAtomId()) {
- return ERROR_NO_ATOM_ID;
- } else if (mPos + LIST_TYPE_OVERHEAD > MAX_EVENT_PAYLOAD) {
- return ERROR_BUFFER_LIMIT_EXCEEDED;
+ /**
+ * Write a long field to this StatsEvent.
+ **/
+ @NonNull
+ public Builder writeLong(final long value) {
+ // Write long typeId byte followed by 8-byte representation of value.
+ writeTypeId(TYPE_LONG);
+ mPos += mBuffer.putLong(mPos, value);
+ mNumElements++;
+ return this;
}
- final int numTags = tags.length;
- final int numUids = uids.length;
- if (numTags != numUids) {
- return ERROR_UID_TAG_COUNT_MISMATCH;
+ /**
+ * Write a float field to this StatsEvent.
+ **/
+ @NonNull
+ public Builder writeFloat(final float value) {
+ // Write float typeId byte followed by 4-byte representation of value.
+ writeTypeId(TYPE_FLOAT);
+ mPos += mBuffer.putFloat(mPos, value);
+ mNumElements++;
+ return this;
}
- int pos = mPos;
- mBuffer[pos] = LIST_TYPE;
- mBuffer[pos + 1] = (byte) numTags;
- pos += LIST_TYPE_OVERHEAD;
- for (int i = 0; i < numTags; i++) {
- final byte[] tagBytes = stringToBytes(tags[i]);
+ /**
+ * Write a String field to this StatsEvent.
+ **/
+ @NonNull
+ public Builder writeString(@NonNull final String value) {
+ // Write String typeId byte, followed by 4-byte representation of number of bytes
+ // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value.
+ final byte[] valueBytes = stringToBytes(value);
+ writeByteArray(valueBytes, TYPE_STRING);
+ return this;
+ }
- if (pos + LIST_TYPE_OVERHEAD + INT_TYPE_SIZE
- + STRING_TYPE_OVERHEAD + tagBytes.length > MAX_EVENT_PAYLOAD) {
- return ERROR_BUFFER_LIMIT_EXCEEDED;
+ /**
+ * Write a byte array field to this StatsEvent.
+ **/
+ @NonNull
+ public Builder writeByteArray(@NonNull final byte[] value) {
+ // Write byte array typeId byte, followed by 4-byte representation of number of bytes
+ // in value, followed by the actual byte array.
+ writeByteArray(value, TYPE_BYTE_ARRAY);
+ return this;
+ }
+
+ private void writeByteArray(@NonNull final byte[] value, final byte typeId) {
+ writeTypeId(typeId);
+ final int numBytes = value.length;
+ mPos += mBuffer.putInt(mPos, numBytes);
+ mPos += mBuffer.putByteArray(mPos, value);
+ mNumElements++;
+ }
+
+ /**
+ * Write an attribution chain field to this StatsEvent.
+ *
+ * The sizes of uids and tags must be equal. The AttributionNode at position i is
+ * made up of uids[i] and tags[i].
+ *
+ * @param uids array of uids in the attribution nodes.
+ * @param tags array of tags in the attribution nodes.
+ **/
+ @NonNull
+ public Builder writeAttributionNode(
+ @NonNull final int[] uids, @NonNull final String[] tags) {
+ final byte numUids = (byte) uids.length;
+ final byte numTags = (byte) tags.length;
+
+ if (numUids != numTags) {
+ mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL;
+ } else if (numUids > MAX_ATTRIBUTION_NODES) {
+ mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
+ } else {
+ // Write attribution chain typeId byte, followed by 1-byte representation of
+ // number of attribution nodes, followed by encoding of each attribution node.
+ writeTypeId(TYPE_ATTRIBUTION_CHAIN);
+ mPos += mBuffer.putByte(mPos, numUids);
+ for (int i = 0; i < numUids; i++) {
+ // Each uid is encoded as 4-byte representation of its int value.
+ mPos += mBuffer.putInt(mPos, uids[i]);
+
+ // Each tag is encoded as 4-byte representation of number of bytes in its
+ // UTF-8 encoding, followed by the actual UTF-8 bytes.
+ final byte[] tagBytes = stringToBytes(tags[i]);
+ mPos += mBuffer.putInt(mPos, tagBytes.length);
+ mPos += mBuffer.putByteArray(mPos, tagBytes);
+ }
+ mNumElements++;
+ }
+ return this;
+ }
+
+ /**
+ * Write a boolean annotation for the last field written.
+ **/
+ @NonNull
+ public Builder addBooleanAnnotation(
+ final byte annotationId, final boolean value) {
+ // Ensure there's a field written to annotate.
+ if (0 == mPosLastField) {
+ mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+ } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
+ mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
+ } else {
+ mPos += mBuffer.putByte(mPos, annotationId);
+ mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN);
+ mPos += mBuffer.putBoolean(mPos, value);
+ mCurrentAnnotationCount++;
+ writeAnnotationCount();
+ }
+ return this;
+ }
+
+ /**
+ * Write an integer annotation for the last field written.
+ **/
+ @NonNull
+ public Builder addIntAnnotation(final byte annotationId, final int value) {
+ if (0 == mPosLastField) {
+ mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+ } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
+ mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
+ } else {
+ mPos += mBuffer.putByte(mPos, annotationId);
+ mPos += mBuffer.putByte(mPos, TYPE_INT);
+ mPos += mBuffer.putInt(mPos, value);
+ mCurrentAnnotationCount++;
+ writeAnnotationCount();
+ }
+ return this;
+ }
+
+ /**
+ * Builds a StatsEvent object with values entered in this Builder.
+ **/
+ @NonNull
+ public StatsEvent build() {
+ if (0L == mTimestampNs) {
+ mErrorMask |= ERROR_NO_TIMESTAMP;
+ }
+ if (0 == mAtomId) {
+ mErrorMask |= ERROR_NO_ATOM_ID;
+ }
+ if (mBuffer.hasOverflowed()) {
+ mErrorMask |= ERROR_OVERFLOW;
+ }
+ if (mNumElements > MAX_NUM_ELEMENTS) {
+ mErrorMask |= ERROR_TOO_MANY_FIELDS;
}
- mBuffer[pos] = LIST_TYPE;
- mBuffer[pos + 1] = 2;
- pos += LIST_TYPE_OVERHEAD;
- mBuffer[pos] = INT_TYPE;
- copyInt(mBuffer, pos + 1, uids[i]);
- pos += INT_TYPE_SIZE;
- mBuffer[pos] = STRING_TYPE;
- copyInt(mBuffer, pos + 1, tagBytes.length);
- System.arraycopy(tagBytes, 0, mBuffer, pos + STRING_TYPE_OVERHEAD, tagBytes.length);
- pos += STRING_TYPE_OVERHEAD + tagBytes.length;
+ int size = mPos;
+ mPos = POS_TIMESTAMP_NS;
+ writeLong(mTimestampNs);
+ writeInt(mAtomId);
+ if (0 == mErrorMask) {
+ mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
+ } else {
+ mBuffer.putByte(0, TYPE_ERRORS);
+ mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
+ mPos += mBuffer.putInt(mPos, mErrorMask);
+ size = mPos;
+ }
+
+ return new StatsEvent(mBuffer, size);
}
- mPos = pos;
- mNumElements++;
- return SUCCESS;
+
+ private void writeTypeId(final byte typeId) {
+ mPosLastField = mPos;
+ mLastType = typeId;
+ mCurrentAnnotationCount = 0;
+ final byte encodedId = (byte) (typeId & 0x0F);
+ mPos += mBuffer.putByte(mPos, encodedId);
+ }
+
+ private void writeAnnotationCount() {
+ // Use first 4 bits for annotation count and last 4 bits for typeId.
+ final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F));
+ mBuffer.putByte(mPosLastField, encodedId);
+ }
+
+ @NonNull
+ private static byte[] stringToBytes(@Nullable final String value) {
+ return (null == value ? "" : value).getBytes(UTF_8);
+ }
}
- /**
- * Returns the byte array containing data in the statsd socket format.
- * @hide
- **/
- @NonNull
- public byte[] getBuffer() {
- // Encode number of elements in the buffer.
- mBuffer[POS_NUM_ELEMENTS] = (byte) mNumElements;
- return mBuffer;
- }
+ private static final class Buffer {
+ private static Object sLock = new Object();
- /**
- * Returns number of bytes used by the buffer.
- * @hide
- **/
- public int size() {
- return mPos;
- }
+ @GuardedBy("sLock")
+ private static Buffer sPool;
- /**
- * Getter for atom id.
- * @hide
- **/
- public int getAtomId() {
- return mAtomId;
- }
+ private final byte[] mBytes = new byte[MAX_PAYLOAD_SIZE];
+ private boolean mOverflow = false;
- @NonNull
- private static byte[] stringToBytes(@Nullable final String value) {
- return (null == value ? "" : value).getBytes(UTF_8);
- }
+ @NonNull
+ private static Buffer obtain() {
+ final Buffer buffer;
+ synchronized (sLock) {
+ buffer = null == sPool ? new Buffer() : sPool;
+ sPool = null;
+ }
+ buffer.reset();
+ return buffer;
+ }
- // Helper methods for copying primitives
- private static void copyInt(@NonNull byte[] buff, int pos, int value) {
- buff[pos] = (byte) (value);
- buff[pos + 1] = (byte) (value >> 8);
- buff[pos + 2] = (byte) (value >> 16);
- buff[pos + 3] = (byte) (value >> 24);
- }
+ private Buffer() {
+ }
- private static void copyLong(@NonNull byte[] buff, int pos, long value) {
- buff[pos] = (byte) (value);
- buff[pos + 1] = (byte) (value >> 8);
- buff[pos + 2] = (byte) (value >> 16);
- buff[pos + 3] = (byte) (value >> 24);
- buff[pos + 4] = (byte) (value >> 32);
- buff[pos + 5] = (byte) (value >> 40);
- buff[pos + 6] = (byte) (value >> 48);
- buff[pos + 7] = (byte) (value >> 56);
+ @NonNull
+ private byte[] getBytes() {
+ return mBytes;
+ }
+
+ private void release() {
+ synchronized (sLock) {
+ if (null == sPool) {
+ sPool = this;
+ }
+ }
+ }
+
+ private void reset() {
+ mOverflow = false;
+ }
+
+ private boolean hasOverflowed() {
+ return mOverflow;
+ }
+
+ /**
+ * Checks for available space in the byte array.
+ *
+ * @param index starting position in the buffer to start the check.
+ * @param numBytes number of bytes to check from index.
+ * @return true if space is available, false otherwise.
+ **/
+ private boolean hasEnoughSpace(final int index, final int numBytes) {
+ final boolean result = index + numBytes < MAX_PAYLOAD_SIZE;
+ if (!result) {
+ mOverflow = true;
+ }
+ return result;
+ }
+
+ /**
+ * Writes a byte into the buffer.
+ *
+ * @param index position in the buffer where the byte is written.
+ * @param value the byte to write.
+ * @return number of bytes written to buffer from this write operation.
+ **/
+ private int putByte(final int index, final byte value) {
+ if (hasEnoughSpace(index, Byte.BYTES)) {
+ mBytes[index] = (byte) (value);
+ return Byte.BYTES;
+ }
+ return 0;
+ }
+
+ /**
+ * Writes a boolean into the buffer.
+ *
+ * @param index position in the buffer where the boolean is written.
+ * @param value the boolean to write.
+ * @return number of bytes written to buffer from this write operation.
+ **/
+ private int putBoolean(final int index, final boolean value) {
+ return putByte(index, (byte) (value ? 1 : 0));
+ }
+
+ /**
+ * Writes an integer into the buffer.
+ *
+ * @param index position in the buffer where the integer is written.
+ * @param value the integer to write.
+ * @return number of bytes written to buffer from this write operation.
+ **/
+ private int putInt(final int index, final int value) {
+ if (hasEnoughSpace(index, Integer.BYTES)) {
+ // Use little endian byte order.
+ mBytes[index] = (byte) (value);
+ mBytes[index + 1] = (byte) (value >> 8);
+ mBytes[index + 2] = (byte) (value >> 16);
+ mBytes[index + 3] = (byte) (value >> 24);
+ return Integer.BYTES;
+ }
+ return 0;
+ }
+
+ /**
+ * Writes a long into the buffer.
+ *
+ * @param index position in the buffer where the long is written.
+ * @param value the long to write.
+ * @return number of bytes written to buffer from this write operation.
+ **/
+ private int putLong(final int index, final long value) {
+ if (hasEnoughSpace(index, Long.BYTES)) {
+ // Use little endian byte order.
+ mBytes[index] = (byte) (value);
+ mBytes[index + 1] = (byte) (value >> 8);
+ mBytes[index + 2] = (byte) (value >> 16);
+ mBytes[index + 3] = (byte) (value >> 24);
+ mBytes[index + 4] = (byte) (value >> 32);
+ mBytes[index + 5] = (byte) (value >> 40);
+ mBytes[index + 6] = (byte) (value >> 48);
+ mBytes[index + 7] = (byte) (value >> 56);
+ return Long.BYTES;
+ }
+ return 0;
+ }
+
+ /**
+ * Writes a float into the buffer.
+ *
+ * @param index position in the buffer where the float is written.
+ * @param value the float to write.
+ * @return number of bytes written to buffer from this write operation.
+ **/
+ private int putFloat(final int index, final float value) {
+ return putInt(index, Float.floatToIntBits(value));
+ }
+
+ /**
+ * Copies a byte array into the buffer.
+ *
+ * @param index position in the buffer where the byte array is copied.
+ * @param value the byte array to copy.
+ * @return number of bytes written to buffer from this write operation.
+ **/
+ private int putByteArray(final int index, @NonNull final byte[] value) {
+ final int numBytes = value.length;
+ if (hasEnoughSpace(index, numBytes)) {
+ System.arraycopy(value, 0, mBytes, index, numBytes);
+ return numBytes;
+ }
+ return 0;
+ }
}
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index ad59ae5..36daa5c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -945,6 +945,94 @@
return null;
}
+ private static class StreamingPictureCallbackHandler implements AutoCloseable,
+ HardwareRenderer.PictureCapturedCallback, Runnable {
+ private final HardwareRenderer mRenderer;
+ private final Callable<OutputStream> mCallback;
+ private final Executor mExecutor;
+ private final ReentrantLock mLock = new ReentrantLock(false);
+ private final ArrayDeque<byte[]> mQueue = new ArrayDeque<>(3);
+ private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream();
+ private boolean mStopListening;
+ private Thread mRenderThread;
+
+ private StreamingPictureCallbackHandler(HardwareRenderer renderer,
+ Callable<OutputStream> callback, Executor executor) {
+ mRenderer = renderer;
+ mCallback = callback;
+ mExecutor = executor;
+ mRenderer.setPictureCaptureCallback(this);
+ }
+
+ @Override
+ public void close() {
+ mLock.lock();
+ mStopListening = true;
+ mLock.unlock();
+ mRenderer.setPictureCaptureCallback(null);
+ }
+
+ @Override
+ public void onPictureCaptured(Picture picture) {
+ mLock.lock();
+ if (mStopListening) {
+ mLock.unlock();
+ mRenderer.setPictureCaptureCallback(null);
+ return;
+ }
+ if (mRenderThread == null) {
+ mRenderThread = Thread.currentThread();
+ }
+ boolean needsInvoke = true;
+ if (mQueue.size() == 3) {
+ mQueue.removeLast();
+ needsInvoke = false;
+ }
+ picture.writeToStream(mByteStream);
+ mQueue.add(mByteStream.toByteArray());
+ mByteStream.reset();
+ mLock.unlock();
+
+ if (needsInvoke) {
+ mExecutor.execute(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ mLock.lock();
+ final byte[] picture = mQueue.poll();
+ final boolean isStopped = mStopListening;
+ mLock.unlock();
+ if (Thread.currentThread() == mRenderThread) {
+ close();
+ throw new IllegalStateException(
+ "ViewDebug#startRenderingCommandsCapture must be given an executor that "
+ + "invokes asynchronously");
+ }
+ if (isStopped) {
+ return;
+ }
+ OutputStream stream = null;
+ try {
+ stream = mCallback.call();
+ } catch (Exception ex) {
+ Log.w("ViewDebug", "Aborting rendering commands capture "
+ + "because callback threw exception", ex);
+ }
+ if (stream != null) {
+ try {
+ stream.write(picture);
+ } catch (IOException ex) {
+ Log.w("ViewDebug", "Aborting rendering commands capture "
+ + "due to IOException writing to output stream", ex);
+ }
+ } else {
+ close();
+ }
+ }
+ }
+
/**
* Begins capturing the entire rendering commands for the view tree referenced by the given
* view. The view passed may be any View in the tree as long as it is attached. That is,
@@ -990,18 +1078,7 @@
}
final HardwareRenderer renderer = attachInfo.mThreadedRenderer;
if (renderer != null) {
- return new PictureCallbackHandler(renderer, (picture -> {
- try {
- OutputStream stream = callback.call();
- if (stream != null) {
- picture.writeToStream(stream);
- return true;
- }
- } catch (Exception ex) {
- // fall through
- }
- return false;
- }), executor);
+ return new StreamingPictureCallbackHandler(renderer, callback, executor);
}
return null;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 20dc234..85bf19f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -439,7 +439,6 @@
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
- boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
boolean mForceNextWindowRelayout;
CountDownLatch mWindowDrawCountDown;
@@ -2123,11 +2122,6 @@
endDragResizing();
destroyHardwareResources();
}
- if (viewVisibility == View.GONE) {
- // After making a window gone, we will count it as being
- // shown for the first time the next time it gets focus.
- mHasHadWindowFocus = false;
- }
}
// Non-visible windows can't hold accessibility focus.
@@ -2823,8 +2817,7 @@
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
+ mWindowAttributes.softInputMode, mWindowAttributes.flags);
}
}
}
@@ -3017,8 +3010,7 @@
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
+ mWindowAttributes.softInputMode, mWindowAttributes.flags);
}
// Clear the forward bit. We can just do this directly, since
// the window manager doesn't care about it.
@@ -3028,7 +3020,6 @@
.softInputMode &=
~WindowManager.LayoutParams
.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- mHasHadWindowFocus = true;
// Refocusing a window that has a focused view should fire a
// focus event for the view since the global focused view changed.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6420d71..7ee53f2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -358,7 +358,7 @@
boolean mActive = false;
/**
- * {@code true} if next {@link #onPostWindowFocus(View, View, int, boolean, int)} needs to
+ * {@code true} if next {@link #onPostWindowFocus(View, View, int, int)} needs to
* restart input.
*/
boolean mRestartOnNextWindowFocus = true;
@@ -1925,13 +1925,12 @@
* @hide
*/
public void onPostWindowFocus(View rootView, View focusedView,
- @SoftInputModeFlags int softInputMode, boolean first, int windowFlags) {
+ @SoftInputModeFlags int softInputMode, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
+ " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
- + " first=" + first + " flags=#"
- + Integer.toHexString(windowFlags));
+ + " flags=#" + Integer.toHexString(windowFlags));
if (mRestartOnNextWindowFocus) {
if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus");
mRestartOnNextWindowFocus = false;
@@ -1947,9 +1946,6 @@
startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
}
}
- if (first) {
- startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN;
- }
if (checkFocusNoStartInput(forceNewFocus)) {
// We need to restart input on the current focus view. This
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 23d1237..3824c22 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -22,7 +22,10 @@
/**
* Manages the cookies used by an application's {@link WebView} instances.
- * Cookies are manipulated according to RFC2109.
+ * <p>
+ * CookieManager represents cookies as strings in the same format as the
+ * HTTP {@code Cookie} and {@code Set-Cookie} header fields (defined in
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>).
*/
public abstract class CookieManager {
/**
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 2e95743..d58d858 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -163,7 +163,6 @@
public class ProgressBar extends View {
private static final int MAX_LEVEL = 10000;
- private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
/** Interpolator used for smooth progress animations. */
private static final DecelerateInterpolator PROGRESS_ANIM_INTERPOLATOR =
@@ -244,8 +243,6 @@
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
- private AccessibilityEventSender mAccessibilityEventSender;
-
/**
* Create a new progress bar with range 0...100 and initial progress of 0.
* @param context the application environment
@@ -1556,7 +1553,7 @@
void onProgressRefresh(float scale, boolean fromUser, int progress) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- scheduleAccessibilityEventSender();
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
}
@@ -2250,9 +2247,6 @@
removeCallbacks(mRefreshProgressRunnable);
mRefreshIsPosted = false;
}
- if (mAccessibilityEventSender != null) {
- removeCallbacks(mAccessibilityEventSender);
- }
// This should come after stopAnimation(), otherwise an invalidate message remains in the
// queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
super.onDetachedFromWindow();
@@ -2285,22 +2279,6 @@
}
}
- /**
- * Schedule a command for sending an accessibility event.
- * </br>
- * Note: A command is used to ensure that accessibility events
- * are sent at most one in a given time frame to save
- * system resources while the progress changes quickly.
- */
- private void scheduleAccessibilityEventSender() {
- if (mAccessibilityEventSender == null) {
- mAccessibilityEventSender = new AccessibilityEventSender();
- } else {
- removeCallbacks(mAccessibilityEventSender);
- }
- postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
- }
-
/** @hide */
@Override
protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
@@ -2324,15 +2302,6 @@
return isIndeterminate() && getWindowVisibility() == VISIBLE && isShown();
}
- /**
- * Command for sending an accessibility event.
- */
- private class AccessibilityEventSender implements Runnable {
- public void run() {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- }
- }
-
private static class ProgressTintInfo {
ColorStateList mIndeterminateTintList;
BlendMode mIndeterminateBlendMode;
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 562cc4f..217693e 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -28,6 +28,7 @@
import android.graphics.Rect;
import android.graphics.Typeface;
import android.icu.text.DisplayContext;
+import android.icu.text.RelativeDateTimeFormatter;
import android.icu.text.SimpleDateFormat;
import android.icu.util.Calendar;
import android.os.Bundle;
@@ -1095,6 +1096,14 @@
node.setText(getDayText(virtualViewId));
node.setContentDescription(getDayDescription(virtualViewId));
+ if (virtualViewId == mToday) {
+ RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance();
+ node.setStateDescription(fmt.format(RelativeDateTimeFormatter.Direction.THIS,
+ RelativeDateTimeFormatter.AbsoluteUnit.DAY));
+ }
+ if (virtualViewId == mActivatedDay) {
+ node.setSelected(true);
+ }
node.setBoundsInParent(mTempRect);
final boolean isDayEnabled = isDayEnabled(virtualViewId);
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index fd2ada0..36bc229 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -49,6 +49,18 @@
return mChangeConfig.forceDisabledSet();
}
+ /**
+ * Returns if a change is enabled or disabled in this config.
+ */
+ public boolean isChangeEnabled(long changeId) {
+ if (mChangeConfig.isForceEnabled(changeId)) {
+ return true;
+ } else if (mChangeConfig.isForceDisabled(changeId)) {
+ return false;
+ }
+ throw new IllegalStateException("Change " + changeId + " is not defined.");
+ }
+
private CompatibilityChangeConfig(Parcel in) {
long[] enabledArray = in.createLongArray();
long[] disabledArray = in.createLongArray();
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.aidl b/core/java/com/android/internal/compat/CompatibilityChangeInfo.aidl
new file mode 100644
index 0000000..3bc7277
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+parcelable CompatibilityChangeInfo;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
new file mode 100644
index 0000000..e48e2df
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class is a parcelable version of {@link com.android.server.compat.Change}.
+ *
+ * @hide
+ */
+public class CompatibilityChangeInfo implements Parcelable {
+ private final long mChangeId;
+ private final @Nullable String mName;
+ private final int mEnableAfterTargetSdk;
+ private final boolean mDisabled;
+
+ public long getId() {
+ return mChangeId;
+ }
+
+ @Nullable
+ public String getName() {
+ return mName;
+ }
+
+ public int getEnableAfterTargetSdk() {
+ return mEnableAfterTargetSdk;
+ }
+
+ public boolean getDisabled() {
+ return mDisabled;
+ }
+
+ public CompatibilityChangeInfo(
+ Long changeId, String name, int enableAfterTargetSdk, boolean disabled) {
+ this.mChangeId = changeId;
+ this.mName = name;
+ this.mEnableAfterTargetSdk = enableAfterTargetSdk;
+ this.mDisabled = disabled;
+ }
+
+ private CompatibilityChangeInfo(Parcel in) {
+ mChangeId = in.readLong();
+ mName = in.readString();
+ mEnableAfterTargetSdk = in.readInt();
+ mDisabled = in.readBoolean();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mChangeId);
+ dest.writeString(mName);
+ dest.writeInt(mEnableAfterTargetSdk);
+ dest.writeBoolean(mDisabled);
+ }
+
+ public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
+ new Parcelable.Creator<CompatibilityChangeInfo>() {
+
+ @Override
+ public CompatibilityChangeInfo createFromParcel(Parcel in) {
+ return new CompatibilityChangeInfo(in);
+ }
+
+ @Override
+ public CompatibilityChangeInfo[] newArray(int size) {
+ return new CompatibilityChangeInfo[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 8391ad2..5857642 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -17,8 +17,10 @@
package com.android.internal.compat;
import android.content.pm.ApplicationInfo;
+import java.util.Map;
parcelable CompatibilityChangeConfig;
+parcelable CompatibilityChangeInfo;
/**
* Platform private API for talking with the PlatformCompat service.
@@ -146,4 +148,21 @@
*
*/
void clearOverrides(in String packageName);
+
+ /**
+ * Get configs for an application.
+ *
+ * @param appInfo The application whose config will be returned.
+ *
+ * @return A {@link CompatibilityChangeConfig}, representing whether a change is enabled for
+ * the given app or not.
+ */
+ CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo);
+
+ /**
+ * List all compatibility changes.
+ *
+ * @return An array of {@link CompatChangeInfo} known to the service.
+ */
+ CompatibilityChangeInfo[] listAllChanges();
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 025e27b..382a254 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -168,9 +168,6 @@
if ((startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
joiner.add("IS_TEXT_EDITOR");
}
- if ((startInputFlags & StartInputFlags.FIRST_WINDOW_FOCUS_GAIN) != 0) {
- joiner.add("FIRST_WINDOW_FOCUS_GAIN");
- }
if ((startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0) {
joiner.add("INITIAL_CONNECTION");
}
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index ba26d8d..5a8d2c2 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -30,7 +30,6 @@
@IntDef(flag = true, value = {
StartInputFlags.VIEW_HAS_FOCUS,
StartInputFlags.IS_TEXT_EDITOR,
- StartInputFlags.FIRST_WINDOW_FOCUS_GAIN,
StartInputFlags.INITIAL_CONNECTION})
public @interface StartInputFlags {
/**
@@ -44,13 +43,8 @@
int IS_TEXT_EDITOR = 2;
/**
- * This is the first time the window has gotten focus.
- */
- int FIRST_WINDOW_FOCUS_GAIN = 4;
-
- /**
* An internal concept to distinguish "start" and "restart". This concept doesn't look well
* documented hence we probably need to revisit this though.
*/
- int INITIAL_CONNECTION = 8;
+ int INITIAL_CONNECTION = 4;
}
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index c0e4e1f..3704ccd 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -59,8 +59,8 @@
return instance_;
}
-static bool IsMemfd(const std::string& path) {
- return android::base::StartsWith(path, "/memfd:");
+static bool IsArtMemfd(const std::string& path) {
+ return android::base::StartsWith(path, "/memfd:/boot-image-methods.art");
}
bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
@@ -91,8 +91,8 @@
return true;
}
- // In-memory files created through memfd_create are allowed.
- if (IsMemfd(path)) {
+ // the in-memory file created by ART through memfd_create is allowed.
+ if (IsArtMemfd(path)) {
return true;
}
@@ -321,8 +321,8 @@
return DetachSocket(fail_fn);
}
- // Children can directly use in-memory files created through memfd_create.
- if (IsMemfd(file_path)) {
+ // Children can directly use the in-memory file created by ART through memfd_create.
+ if (IsArtMemfd(file_path)) {
return;
}
@@ -545,6 +545,10 @@
}
void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
+ // ART creates a file through memfd for optimization purposes. We make sure
+ // there is at most one being created.
+ bool art_memfd_seen = false;
+
// Iterate through the list of file descriptors we've already recorded
// and check whether :
//
@@ -577,6 +581,14 @@
// FD.
}
+ if (IsArtMemfd(it->second->file_path)) {
+ if (art_memfd_seen) {
+ fail_fn("ART fd already seen: " + it->second->file_path);
+ } else {
+ art_memfd_seen = true;
+ }
+ }
+
++it;
// Finally, remove the FD from the set of open_fds. We do this last because
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 6ab0fc9..74ced89 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -3,8 +3,10 @@
# Metrics
joeo@google.com
singhtejinder@google.com
+yanmin@google.com
yaochen@google.com
yro@google.com
+zhouwenjie@google.com
# Settings UI
per-file settings_enums.proto=tmfang@google.com
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 7d0629e..047c095 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -42,6 +42,7 @@
import "frameworks/base/core/proto/android/service/battery.proto";
import "frameworks/base/core/proto/android/service/batterystats.proto";
import "frameworks/base/core/proto/android/service/diskstats.proto";
+import "frameworks/base/core/proto/android/service/dropbox.proto";
import "frameworks/base/core/proto/android/service/graphicsstats.proto";
import "frameworks/base/core/proto/android/service/netstats.proto";
import "frameworks/base/core/proto/android/service/notification.proto";
@@ -329,6 +330,22 @@
(section).userdebug_and_eng_only = true
];
+ // Dropbox entries split by tags.
+ optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_data_app_crashes = 3027 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "dropbox --proto data_app_crash"
+ ];
+
+ optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_data_app_anr = 3028 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "dropbox --proto data_app_anr"
+ ];
+
+ optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_data_app_native_crash = 3029 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "dropbox --proto data_app_native_crash"
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index a346a63..69e67d1 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -231,7 +231,7 @@
optional WindowTokenProto window_token = 2;
optional bool last_surface_showing = 3;
optional bool is_waiting_for_transition_start = 4;
- optional bool is_really_animating = 5;
+ optional bool is_animating = 5;
optional AppWindowThumbnailProto thumbnail = 6;
optional bool fills_parent = 7;
optional bool app_stopped = 8;
diff --git a/core/proto/android/service/dropbox.proto b/core/proto/android/service/dropbox.proto
new file mode 100644
index 0000000..29fe62b
--- /dev/null
+++ b/core/proto/android/service/dropbox.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.service.dropbox;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+// Dump from com.android.server.DropboxManagerService.java.
+message DropBoxManagerServiceDumpProto {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ message Entry {
+ // Time when entry was originally created.
+ optional int64 time_ms = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ] ;
+ optional bytes data = 2;
+ }
+ repeated Entry entries = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 11a5062..5bb1801 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4647,6 +4647,13 @@
android:protectionLevel="normal" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <!-- @hide Allow the caller to collect debugging data from processes that otherwise
+ would require USAGE_STATS. Before sharing this data with other apps, holders
+ of this permission are REQUIRED to themselves check that the caller has
+ PACKAGE_USAGE_STATS and OP_GET_USAGE_STATS. -->
+ <permission android:name="android.permission.PEEK_DROPBOX_DATA"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index f14f289..e140ad2 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -82,22 +82,23 @@
final AssetFileDescriptor afd = new AssetFileDescriptor(
new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
- when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd);
+ when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any(), any())).thenReturn(
+ afd);
}
- private static void assertImageAspectAndContents(Bitmap bitmap) {
+ private static void assertImageAspectAndContents(int width, int height, Bitmap bitmap) {
// And correct aspect ratio
- final int before = (100 * 1280) / 960;
+ final int before = (100 * width) / height;
final int after = (100 * bitmap.getWidth()) / bitmap.getHeight();
assertEquals(before, after);
// And scaled correctly
final int halfX = bitmap.getWidth() / 2;
final int halfY = bitmap.getHeight() / 2;
- assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10));
- assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10));
- assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10));
- assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10));
+ assertEquals(Color.BLUE, bitmap.getPixel(halfX - 5, halfY - 5));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 5, halfY - 5));
+ assertEquals(Color.RED, bitmap.getPixel(halfX - 5, halfY + 5));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 5, halfY + 5));
}
@Test
@@ -112,7 +113,7 @@
assertEquals(1280, res.getWidth());
assertEquals(960, res.getHeight());
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(1280, 960, res);
}
@Test
@@ -127,7 +128,7 @@
assertTrue(res.getWidth() <= 640);
assertTrue(res.getHeight() <= 480);
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(1280, 960, res);
}
@Test
@@ -142,7 +143,7 @@
assertTrue(res.getWidth() <= 640);
assertTrue(res.getHeight() <= 480);
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(1280, 960, res);
}
@Test
@@ -157,7 +158,23 @@
assertEquals(32, res.getWidth());
assertEquals(24, res.getHeight());
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(32, 24, res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Large() throws Exception {
+ // Test very large and extreme ratio image
+ initImage(1080, 30000);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(1080, 540), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be much smaller
+ assertTrue(res.getWidth() <= 2160);
+ assertTrue(res.getHeight() <= 1080);
+
+ assertImageAspectAndContents(1080, 30000, res);
}
@Test
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 1f2dfe0..0c83390 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -388,6 +388,13 @@
assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3);
assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+ DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE, false);
+ properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE);
}
@Test
diff --git a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
index 1bd8ff6..5f640be 100644
--- a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
+++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
@@ -93,12 +93,14 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String callingFeatureId, IBinder callerToken) {
return AppOpsManager.MODE_ALLOWED;
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String callingFeatureId, IBinder callerToken) {
return AppOpsManager.MODE_ALLOWED;
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index b3f6fdb..364e4ea 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -46,10 +46,12 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.view.WindowManagerGlobal;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -70,6 +72,8 @@
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
+@Presubmit
+@FlakyTest(bugId = 143153552)
public class ActivityThreadClientTest {
@Test
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index dceb243..80098c5 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -174,6 +174,7 @@
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" />
<assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="incidentd" />
<assign-permission name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL" uid="incidentd" />
+ <assign-permission name="android.permission.PEEK_DROPBOX_DATA" uid="incidentd" />
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 342259d..ecdf537 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -169,12 +169,6 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-1822611824": {
- "message": "\tRemove token=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
- },
"-1797409732": {
"message": "Skipping %s because %s",
"level": "VERBOSE",
@@ -421,6 +415,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1248645819": {
+ "message": "\tAdd container=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"-1219773477": {
"message": "setInputConsumerEnabled(%s): mCanceled=%b",
"level": "DEBUG",
@@ -493,12 +493,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "-1099052739": {
- "message": "\tAdd token=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
- },
"-1089874824": {
"message": "SURFACE SHOW (performLayout): %s",
"level": "INFO",
@@ -721,6 +715,12 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-633961578": {
+ "message": "applyAnimation: transition animation is disabled or skipped. container=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-622997754": {
"message": "postWindowRemoveCleanupLocked: %s",
"level": "VERBOSE",
@@ -883,12 +883,6 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-253016819": {
- "message": "applyAnimation: transition animation is disabled or skipped. atoken=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-251259736": {
"message": "No longer freezing: %s",
"level": "VERBOSE",
@@ -961,6 +955,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/Session.java"
},
+ "-33096143": {
+ "message": "applyAnimation: transition animation is disabled or skipped. container=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"-29233992": {
"message": "SURFACE CLEAR CROP: %s",
"level": "INFO",
@@ -1375,6 +1375,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
+ "638429464": {
+ "message": "\tRemove container=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"644675193": {
"message": "Real start recents",
"level": "DEBUG",
@@ -1465,12 +1471,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "815803557": {
- "message": "applyAnimation: atoken=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"829434921": {
"message": "Draw state now committed in %s",
"level": "VERBOSE",
@@ -1543,6 +1543,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "972354148": {
+ "message": "\tcontainer=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"1001904964": {
"message": "***** BOOT TIMEOUT: forcing display enabled",
"level": "WARN",
@@ -1675,12 +1681,6 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "1358786604": {
- "message": "No thumbnail header bitmap for: %d",
- "level": "DEBUG",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1364498663": {
"message": "notifyAppResumed: wasStopped=%b %s",
"level": "VERBOSE",
@@ -1795,11 +1795,11 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "1531527061": {
- "message": "createAnimationAdapter(): token=%s",
+ "1528528509": {
+ "message": "No thumbnail header bitmap for: %s",
"level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"1563755163": {
"message": "Permission Denial: %s from pid=%d, uid=%d requires %s",
@@ -1819,6 +1819,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "1584270979": {
+ "message": "applyAnimation: container=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"1589610525": {
"message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s",
"level": "VERBOSE",
@@ -1909,11 +1915,11 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1804869745": {
+ "1831008694": {
"message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s",
"level": "DEBUG",
"group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
},
"1836214582": {
"message": "startingData was nulled out before handling mAddStartingWindow: %s",
@@ -1939,12 +1945,6 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/DisplayPolicy.java"
},
- "1865246212": {
- "message": "\tapp=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
- },
"1866772666": {
"message": "SAFE MODE not enabled",
"level": "INFO",
@@ -2017,6 +2017,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "2022422429": {
+ "message": "createAnimationAdapter(): container=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"2028163120": {
"message": "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s isEntrance=%s Callers=%s",
"level": "VERBOSE",
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index ec83a19..1eb089d 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -136,7 +136,6 @@
const Paint* paint) override;
virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override;
- virtual bool drawTextAbsolutePos() const override { return true; }
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index b98ffca..c138a32 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -95,18 +95,10 @@
void operator()(size_t start, size_t end) {
auto glyphFunc = [&](uint16_t* text, float* positions) {
- if (canvas->drawTextAbsolutePos()) {
- for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
- text[textIndex++] = layout.getGlyphId(i);
- positions[posIndex++] = x + layout.getX(i);
- positions[posIndex++] = y + layout.getY(i);
- }
- } else {
- for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
- text[textIndex++] = layout.getGlyphId(i);
- positions[posIndex++] = layout.getX(i);
- positions[posIndex++] = layout.getY(i);
- }
+ for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
+ text[textIndex++] = layout.getGlyphId(i);
+ positions[posIndex++] = x + layout.getX(i);
+ positions[posIndex++] = y + layout.getY(i);
}
};
@@ -166,9 +158,6 @@
minikin::MinikinRect bounds;
layout.getBounds(&bounds);
- if (!drawTextAbsolutePos()) {
- bounds.offset(x, y);
- }
// Set align to left for drawing, as we don't want individual
// glyphs centered or right-aligned; the offset above takes
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index b90a4a3..27dfed3 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -253,15 +253,6 @@
virtual void drawPicture(const SkPicture& picture) = 0;
/**
- * Specifies if the positions passed to ::drawText are absolute or relative
- * to the (x,y) value provided.
- *
- * If true the (x,y) values are ignored. Otherwise, those (x,y) values need
- * to be added to each glyph's position to get its absolute position.
- */
- virtual bool drawTextAbsolutePos() const = 0;
-
- /**
* Draws a VectorDrawable onto the canvas.
*/
virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c3aae7d..ca9d4d3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -230,7 +230,7 @@
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
- private static final long GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000;
+ private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
private final Context mContext;
@@ -630,7 +630,10 @@
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
LocationRequest currentLocationRequest = new LocationRequest(locationRequest)
- .setNumUpdates(1).setExpireIn(GET_CURRENT_LOCATION_TIMEOUT_MS);
+ .setNumUpdates(1);
+ if (currentLocationRequest.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+ currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ }
GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor,
consumer);
@@ -684,6 +687,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, listener, looper);
}
@@ -714,6 +718,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, listener, looper);
}
@@ -740,6 +745,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, pendingIntent);
}
@@ -767,6 +773,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, pendingIntent);
}
@@ -2422,7 +2429,7 @@
mAlarmManager = alarmManager;
mAlarmManager.set(
ELAPSED_REALTIME,
- SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_TIMEOUT_MS,
+ SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_MAX_TIMEOUT_MS,
"GetCurrentLocation",
this,
null);
@@ -2750,9 +2757,14 @@
protected boolean registerService() throws RemoteException {
Preconditions.checkState(mListenerTransport == null);
- mListenerTransport = new GnssStatusListener();
- return mService.registerGnssStatusCallback(mListenerTransport,
- mContext.getPackageName(), mContext.getFeatureId());
+ GnssStatusListener transport = new GnssStatusListener();
+ if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
+ mContext.getFeatureId())) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
}
@Override
@@ -2810,10 +2822,14 @@
protected boolean registerService() throws RemoteException {
Preconditions.checkState(mListenerTransport == null);
- mListenerTransport = new GnssMeasurementsListener();
- return mService.addGnssMeasurementsListener(mListenerTransport,
- mContext.getPackageName(), mContext.getFeatureId(),
- "gnss measurement callback");
+ GnssMeasurementsListener transport = new GnssMeasurementsListener();
+ if (mService.addGnssMeasurementsListener(transport, mContext.getPackageName(),
+ mContext.getFeatureId(), "gnss measurement callback")) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
}
@Override
@@ -2847,10 +2863,14 @@
protected boolean registerService() throws RemoteException {
Preconditions.checkState(mListenerTransport == null);
- mListenerTransport = new GnssNavigationMessageListener();
- return mService.addGnssNavigationMessageListener(mListenerTransport,
- mContext.getPackageName(), mContext.getFeatureId(),
- "gnss navigation callback");
+ GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
+ if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
+ mContext.getFeatureId(), "gnss navigation callback")) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
}
@Override
@@ -2884,9 +2904,14 @@
protected boolean registerService() throws RemoteException {
Preconditions.checkState(mListenerTransport == null);
- mListenerTransport = new BatchedLocationCallback();
- return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(),
- mContext.getFeatureId(), "batched location callback");
+ BatchedLocationCallback transport = new BatchedLocationCallback();
+ if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
+ mContext.getFeatureId(), "batched location callback")) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
}
@Override
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0f38f7f..3abd2c2 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -161,6 +161,7 @@
private boolean mExplicitFastestInterval = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private long mExpireAt = Long.MAX_VALUE; // no expiry
+ private long mExpireIn = Long.MAX_VALUE; // no expiry
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -268,6 +269,7 @@
mFastestInterval = src.mFastestInterval;
mExplicitFastestInterval = src.mExplicitFastestInterval;
mExpireAt = src.mExpireAt;
+ mExpireIn = src.mExpireIn;
mNumUpdates = src.mNumUpdates;
mSmallestDisplacement = src.mSmallestDisplacement;
mProvider = src.mProvider;
@@ -342,7 +344,7 @@
* @throws IllegalArgumentException if the interval is less than zero
*/
public @NonNull LocationRequest setInterval(long millis) {
- checkInterval(millis);
+ Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
mInterval = millis;
if (!mExplicitFastestInterval) {
mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR);
@@ -370,9 +372,7 @@
*
* @param enabled Enable or disable low power mode
* @return the same object, so that setters can be chained
- * @hide
*/
- @SystemApi
public @NonNull LocationRequest setLowPowerMode(boolean enabled) {
mLowPowerMode = enabled;
return this;
@@ -380,10 +380,7 @@
/**
* Returns true if low power mode is enabled.
- *
- * @hide
*/
- @SystemApi
public boolean isLowPowerMode() {
return mLowPowerMode;
}
@@ -431,93 +428,80 @@
* <p>An interval of 0 is allowed, but not recommended, since
* location updates may be extremely fast on future implementations.
*
- * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
+ * <p>If the fastest interval set is slower than {@link #setInterval},
* then your effective fastest interval is {@link #setInterval}.
*
- * @param millis fastest interval for updates in milliseconds, exact
+ * @param millis fastest interval for updates in milliseconds
* @return the same object, so that setters can be chained
* @throws IllegalArgumentException if the interval is less than zero
*/
public @NonNull LocationRequest setFastestInterval(long millis) {
- checkInterval(millis);
+ Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
mExplicitFastestInterval = true;
mFastestInterval = millis;
return this;
}
/**
- * Get the fastest interval of this request, in milliseconds.
+ * Get the fastest interval of this request in milliseconds. The system will never provide
+ * location updates faster than the minimum of the fastest interval and {@link #getInterval}.
*
- * <p>The system will never provide location updates faster
- * than the minimum of {@link #getFastestInterval} and
- * {@link #getInterval}.
- *
- * @return fastest interval in milliseconds, exact
+ * @return fastest interval in milliseconds
*/
public long getFastestInterval() {
return mFastestInterval;
}
/**
- * Set the duration of this request, in milliseconds.
+ * Set the expiration time of this request in milliseconds of realtime since boot. Values in the
+ * past are allowed, but indicate that the request has already expired. The location manager
+ * will automatically stop updates after the request expires.
*
- * <p>The duration begins immediately (and not when the request
- * is passed to the location manager), so call this method again
- * if the request is re-used at a later time.
+ * @param millis expiration time of request in milliseconds since boot
+ * @return the same object, so that setters can be chained
+ * @see SystemClock#elapsedRealtime()
+ * @deprecated Prefer {@link #setExpireIn(long)}.
+ */
+ @Deprecated
+ public @NonNull LocationRequest setExpireAt(long millis) {
+ mExpireAt = Math.max(millis, 0);
+ return this;
+ }
+
+ /**
+ * Get the request expiration time in milliseconds of realtime since boot.
*
- * <p>The location manager will automatically stop updates after
- * the request expires.
- *
- * <p>The duration includes suspend time. Values less than 0
- * are allowed, but indicate that the request has already expired.
+ * @return request expiration time in milliseconds since boot
+ * @see SystemClock#elapsedRealtime()
+ * @deprecated Prefer {@link #getExpireIn()}.
+ */
+ @Deprecated
+ public long getExpireAt() {
+ return mExpireAt;
+ }
+
+ /**
+ * Set the duration of this request in milliseconds of realtime. Values less than 0 are allowed,
+ * but indicate that the request has already expired. The location manager will automatically
+ * stop updates after the request expires.
*
* @param millis duration of request in milliseconds
* @return the same object, so that setters can be chained
+ * @see SystemClock#elapsedRealtime()
*/
public @NonNull LocationRequest setExpireIn(long millis) {
- long elapsedRealtime = SystemClock.elapsedRealtime();
-
- // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
- if (millis > Long.MAX_VALUE - elapsedRealtime) {
- mExpireAt = Long.MAX_VALUE;
- } else {
- mExpireAt = millis + elapsedRealtime;
- }
-
- if (mExpireAt < 0) mExpireAt = 0;
+ mExpireIn = millis;
return this;
}
/**
- * Set the request expiration time, in millisecond since boot.
+ * Get the request expiration duration in milliseconds of realtime.
*
- * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
- *
- * <p>The location manager will automatically stop updates after
- * the request expires.
- *
- * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
- * are allowed, but indicate that the request has already expired.
- *
- * @param millis expiration time of request, in milliseconds since boot including suspend
- * @return the same object, so that setters can be chained
+ * @return request expiration duration in milliseconds
+ * @see SystemClock#elapsedRealtime()
*/
- public @NonNull LocationRequest setExpireAt(long millis) {
- mExpireAt = millis;
- if (mExpireAt < 0) mExpireAt = 0;
- return this;
- }
-
- /**
- * Get the request expiration time, in milliseconds since boot.
- *
- * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
- * the time until expiration.
- *
- * @return expiration time of request, in milliseconds since boot including suspend
- */
- public long getExpireAt() {
- return mExpireAt;
+ public long getExpireIn() {
+ return mExpireIn;
}
/**
@@ -638,13 +622,6 @@
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private static void checkInterval(long millis) {
- if (millis < 0) {
- throw new IllegalArgumentException("invalid interval: " + millis);
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static void checkQuality(int quality) {
switch (quality) {
case ACCURACY_FINE:
@@ -682,6 +659,7 @@
request.setFastestInterval(in.readLong());
request.setInterval(in.readLong());
request.setExpireAt(in.readLong());
+ request.setExpireIn(in.readLong());
request.setNumUpdates(in.readInt());
request.setSmallestDisplacement(in.readFloat());
request.setHideFromAppOps(in.readInt() != 0);
@@ -711,6 +689,7 @@
parcel.writeLong(mFastestInterval);
parcel.writeLong(mInterval);
parcel.writeLong(mExpireAt);
+ parcel.writeLong(mExpireIn);
parcel.writeInt(mNumUpdates);
parcel.writeFloat(mSmallestDisplacement);
parcel.writeInt(mHideFromAppOps ? 1 : 0);
@@ -753,9 +732,11 @@
s.append(" fastest=");
TimeUtils.formatDuration(mFastestInterval, s);
if (mExpireAt != Long.MAX_VALUE) {
- long expireIn = mExpireAt - SystemClock.elapsedRealtime();
+ s.append(" expireAt=").append(TimeUtils.formatUptime(mExpireAt));
+ }
+ if (mExpireIn != Long.MAX_VALUE) {
s.append(" expireIn=");
- TimeUtils.formatDuration(expireIn, s);
+ TimeUtils.formatDuration(mExpireIn, s);
}
if (mNumUpdates != Integer.MAX_VALUE) {
s.append(" num=").append(mNumUpdates);
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index fe0f669..cd45e8e 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -17,10 +17,8 @@
java_sdk_library {
name: "com.android.location.provider",
srcs: ["java/**/*.java"],
- api_srcs: [":framework-all-sources"],
libs: [
"androidx.annotation_annotation",
- "framework-all",
],
api_packages: ["com.android.location.provider"],
}
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index f813d1b..7bc2b31 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -192,13 +192,15 @@
mMaxImages = maxImages;
- if (format == ImageFormat.UNKNOWN) {
- format = SurfaceUtils.getSurfaceFormat(surface);
- }
// Note that the underlying BufferQueue is working in synchronous mode
// to avoid dropping any buffers.
mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format);
+ // nativeInit internally overrides UNKNOWN format. So does surface format query after
+ // nativeInit and before getEstimatedNativeAllocBytes().
+ if (format == ImageFormat.UNKNOWN) {
+ format = SurfaceUtils.getSurfaceFormat(surface);
+ }
// Estimate the native buffer allocation size and register it so it gets accounted for
// during GC. Note that this doesn't include the buffers required by the buffer queue
// itself and the buffers requested by the producer.
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index cc5ddeb..5d2bdd7 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -62,7 +63,8 @@
* method before the rest of the methods in this class. This method may be
* time-consuming.
*
- * @param path The path of the input media file.
+ * @param path The path, or the URI (doesn't support streaming source currently)
+ * of the input media file.
* @throws IllegalArgumentException If the path is invalid.
*/
public void setDataSource(String path) throws IllegalArgumentException {
@@ -70,6 +72,15 @@
throw new IllegalArgumentException("null path");
}
+ final Uri uri = Uri.parse(path);
+ final String scheme = uri.getScheme();
+ if ("file".equals(scheme)) {
+ path = uri.getPath();
+ } else if (scheme != null) {
+ setDataSource(path, new HashMap<String, String>());
+ return;
+ }
+
try (FileInputStream is = new FileInputStream(path)) {
FileDescriptor fd = is.getFD();
setDataSource(fd, 0, 0x7ffffffffffffffL);
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index 5b62f16..d942bb6 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -16,8 +16,8 @@
package android.media;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
@@ -206,12 +206,13 @@
}
static Size parseSize(Object o, Size fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
return Size.parseSize((String) o);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
}
Log.w(TAG, "could not parse size '" + o + "'");
return fallback;
@@ -226,14 +227,15 @@
return Integer.parseInt(s);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
}
Log.w(TAG, "could not parse integer '" + o + "'");
return fallback;
}
static Range<Integer> parseIntRange(Object o, Range<Integer> fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -246,8 +248,6 @@
return Range.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse integer range '" + o + "'");
@@ -255,6 +255,9 @@
}
static Range<Long> parseLongRange(Object o, Range<Long> fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -267,8 +270,6 @@
return Range.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse long range '" + o + "'");
@@ -276,6 +277,9 @@
}
static Range<Rational> parseRationalRange(Object o, Range<Rational> fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -288,8 +292,6 @@
return Range.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse rational range '" + o + "'");
@@ -297,6 +299,9 @@
}
static Pair<Size, Size> parseSizeRange(Object o) {
+ if (o == null) {
+ return null;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -309,8 +314,6 @@
return Pair.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return null;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse size range '" + o + "'");
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 0228dc9..2257747 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -16,6 +16,13 @@
package android.media.tv.tuner;
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
/**
* Tuner is used to interact with tuner devices.
*
@@ -25,11 +32,41 @@
private static final String TAG = "MediaTvTuner";
private static final boolean DEBUG = false;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3,
+ FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS,
+ FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT})
+ public @interface FrontendType {}
+
+ public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
+ public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG;
+ public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC;
+ public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3;
+ public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC;
+ public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS;
+ public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT;
+ public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS;
+ public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
+ public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
+ FRONTEND_EVENT_TYPE_LOST_LOCK})
+ public @interface FrontendEventType {}
+
+ public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
+ public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+ public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
+
static {
System.loadLibrary("media_tv_tuner");
nativeInit();
}
+ private FrontendCallback mFrontendCallback;
+ private List<Integer> mFrontendIds;
+
public Tuner() {
nativeSetup();
}
@@ -48,4 +85,48 @@
* Native setup.
*/
private native void nativeSetup();
+
+ /**
+ * Native method to get all frontend IDs.
+ */
+ private native List<Integer> nativeGetFrontendIds();
+
+ /**
+ * Native method to open frontend of the given ID.
+ */
+ private native Frontend nativeOpenFrontendById(int id);
+
+
+ /**
+ * Frontend Callback.
+ */
+ public interface FrontendCallback {
+
+ /**
+ * Invoked when there is a frontend event.
+ */
+ void onEvent(int frontendEventType);
+ }
+
+ protected static class Frontend {
+ int mId;
+ private Frontend(int id) {
+ mId = id;
+ }
+ }
+
+ private List<Integer> getFrontendIds() {
+ mFrontendIds = nativeGetFrontendIds();
+ return mFrontendIds;
+ }
+
+ private Frontend openFrontendById(int id) {
+ if (mFrontendIds == null) {
+ getFrontendIds();
+ }
+ if (!mFrontendIds.contains(id)) {
+ return null;
+ }
+ return nativeOpenFrontendById(id);
+ }
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index a596d89..2f53cbb 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -99,13 +99,14 @@
"android_media_Utils.cpp",
],
+ header_libs: [
+ "libgui_headers",
+ ],
+
shared_libs: [
"liblog",
- "libgui",
- "libnativewindow",
"libui",
"libutils",
- "android.hidl.token@1.0-utils",
],
include_dirs: [
@@ -132,6 +133,7 @@
shared_libs: [
"android.hardware.tv.tuner@1.0",
"libandroid_runtime",
+ "libhidlbase",
"liblog",
"libutils",
],
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index d499eee..ba133fc 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -25,10 +25,13 @@
#pragma GCC diagnostic ignored "-Wunused-function"
+using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::Result;
struct fields_t {
jfieldID context;
+ jmethodID frontendInitID;
};
static fields_t gFields;
@@ -69,6 +72,50 @@
return mTuner;
}
+jobject JTuner::getFrontendIds() {
+ ALOGD("JTuner::getFrontendIds()");
+ hidl_vec<FrontendId> feIds;
+ mTuner->getFrontendIds([&](Result, const hidl_vec<FrontendId>& frontendIds) {
+ feIds = frontendIds;
+ });
+ if (feIds.size() == 0) {
+ ALOGW("Frontend isn't available");
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass arrayListClazz = env->FindClass("java/util/ArrayList");
+ jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
+ jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V"));
+
+ jclass integerClazz = env->FindClass("java/lang/Integer");
+ jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
+
+ for (int i=0; i < feIds.size(); i++) {
+ jobject idObj = env->NewObject(integerClazz, intInit, feIds[i]);
+ env->CallBooleanMethod(obj, arrayListAdd, idObj);
+ }
+ return obj;
+}
+
+jobject JTuner::openFrontendById(int id) {
+ mTuner->openFrontendById(id, [&](Result, const sp<IFrontend>& frontend) {
+ mFe = frontend;
+ });
+ if (mFe == nullptr) {
+ ALOGE("Failed to open frontend");
+ return NULL;
+ }
+
+ jint jId = (jint) id;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ // TODO: add more fields to frontend
+ return env->NewObject(
+ env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
+ gFields.frontendInitID,
+ (jint) jId);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -99,6 +146,9 @@
gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
CHECK(gFields.context != NULL);
+
+ jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend");
+ gFields.frontendInitID = env->GetMethodID(frontendClazz, "<init>", "(I)V");
}
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
@@ -106,9 +156,23 @@
setTuner(env,thiz, tuner);
}
+static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getFrontendIds();
+}
+
+static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openFrontendById(id);
+}
+
static const JNINativeMethod gMethods[] = {
{ "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
+ { "nativeGetFrontendIds", "()Ljava/util/List;",
+ (void *)android_media_tv_Tuner_get_frontend_ids },
+ { "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+ (void *)android_media_tv_Tuner_open_frontend_by_id },
};
static int register_android_media_tv_Tuner(JNIEnv *env) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index e7d5924..1425542 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -22,6 +22,8 @@
#include "jni.h"
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::IFrontend;
using ::android::hardware::tv::tuner::V1_0::ITuner;
namespace android {
@@ -29,6 +31,8 @@
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
sp<ITuner> getTunerService();
+ jobject getFrontendIds();
+ jobject openFrontendById(int id);
protected:
virtual ~JTuner();
@@ -36,6 +40,7 @@
jclass mClass;
jweak mObject;
static sp<ITuner> mTuner;
+ sp<IFrontend> mFe;
};
} // namespace android
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 6b03e4d..3b25787 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -17,7 +17,5 @@
java_sdk_library {
name: "com.android.mediadrm.signer",
srcs: ["java/**/*.java"],
- api_srcs: [":framework-all-sources"],
- libs: ["framework-all"],
api_packages: ["com.android.mediadrm.signer"],
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 74bf1a2..de353bf 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -42,6 +42,7 @@
public class MediaInserterTest extends InstrumentationTestCase {
+ private static final String TEST_FEATURE_ID = "testFeature";
private MediaInserter mMediaInserter;
private static final int TEST_BUFFER_SIZE = 10;
private @Mock IContentProvider mMockProvider;
@@ -86,7 +87,8 @@
MockitoAnnotations.initMocks(this);
final ContentProviderClient client = new ContentProviderClient(
- getInstrumentation().getContext().getContentResolver(), mMockProvider, true);
+ getInstrumentation().getContext().createFeatureContext(TEST_FEATURE_ID)
+ .getContentResolver(), mMockProvider, true);
mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE);
mPackageName = getInstrumentation().getContext().getPackageName();
mFilesCounter = 0;
@@ -142,31 +144,36 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
- verify(mMockProvider, never()).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, never()).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsEqualToBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE);
fillBuffer(sVideoUri, TEST_BUFFER_SIZE);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsMoreThanBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2);
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
@@ -176,7 +183,8 @@
@SmallTest
public void testFlushAllWithSomeContents() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
@@ -184,12 +192,14 @@
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
mMediaInserter.flushAll();
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsAfterFlushAll() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
@@ -202,15 +212,20 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sFilesUri), any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sAudioUri), any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sVideoUri), any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sImagesUri), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sFilesUri),
+ any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sAudioUri),
+ any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sVideoUri),
+ any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sImagesUri),
+ any())).thenReturn(1);
for (int i = 0; i < TEST_BUFFER_SIZE; ++i) {
fillBuffer(sFilesUri, 1);
@@ -219,9 +234,13 @@
fillBuffer(sImagesUri, 4);
}
- verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eqUri(sFilesUri), any());
- verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eqUri(sAudioUri), any());
- verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eqUri(sVideoUri), any());
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eqUri(sImagesUri), any());
+ verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sFilesUri), any());
+ verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sAudioUri), any());
+ verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sVideoUri), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sImagesUri), any());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
copy to packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
index 4e4c06e..c40eda9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.systemui;
-import dagger.Binds;
+import com.android.systemui.dagger.DefaultActivityBinder;
+import com.android.systemui.dagger.DefaultServiceBinder;
+
import dagger.Module;
/**
- * Dagger Module that collects related sub-modules together.
+ * Supply Activities, Services, and SystemUI Objects for CarSystemUI.
*/
-@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
-public abstract class ComponentBinder {
- /** */
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
+@Module(includes = {
+ DefaultActivityBinder.class,
+ DefaultServiceBinder.class,
+ CarSystemUIBinder.class})
+public class CarComponentBinder {
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 8e0a3eb..59b1068 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -16,19 +16,293 @@
package com.android.systemui;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.util.DisplayMetrics;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.car.CarNavigationBar;
+import com.android.systemui.navigationbar.car.CarNavigationBarController;
+import com.android.systemui.pip.PipUI;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsModule;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.car.CarStatusBar;
+import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.volume.VolumeUI;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
import dagger.Binds;
+import dagger.Lazy;
import dagger.Module;
+import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/** Binder for car specific {@link SystemUI} modules. */
-@Module
+@Module(includes = {RecentsModule.class})
public abstract class CarSystemUIBinder {
/** */
@Binds
@IntoMap
@ClassKey(CarNavigationBar.class)
public abstract SystemUI bindCarNavigationBar(CarNavigationBar sysui);
+
+ /** Inject into GarbageMonitor.Service. */
+ @Binds
+ @IntoMap
+ @ClassKey(GarbageMonitor.Service.class)
+ public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service);
+
+ /** Inject into KeyguardViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardViewMediator.class)
+ public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+
+ /** Inject into LatencyTests. */
+ @Binds
+ @IntoMap
+ @ClassKey(LatencyTester.class)
+ public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+
+ /** Inject into PipUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PipUI.class)
+ public abstract SystemUI bindPipUI(PipUI sysui);
+
+ /** Inject into PowerUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PowerUI.class)
+ public abstract SystemUI bindPowerUI(PowerUI sysui);
+
+ /** Inject into Recents. */
+ @Binds
+ @IntoMap
+ @ClassKey(Recents.class)
+ public abstract SystemUI bindRecents(Recents sysui);
+
+ /** Inject into ScreenDecorations. */
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenDecorations.class)
+ public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+
+ /** Inject into StatusBar. */
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBar.class)
+ public abstract SystemUI bindsStatusBar(CarStatusBar sysui);
+
+ /** Inject into StatusBarGoogle. */
+ @Binds
+ @IntoMap
+ @ClassKey(CarStatusBar.class)
+ public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui);
+
+ /** Inject into VolumeUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(VolumeUI.class)
+ public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+
+ /**
+ * Provides our instance of StatusBar which is considered optional.
+ */
+ @Provides
+ @Singleton
+ static CarStatusBar provideStatusBar(
+ Context context,
+ FeatureFlags featureFlags,
+ LightBarController lightBarController,
+ AutoHideController autoHideController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ StatusBarIconController statusBarIconController,
+ DozeLog dozeLog,
+ InjectionInflationController injectionInflationController,
+ PulseExpansionHandler pulseExpansionHandler,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator,
+ KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
+ HeadsUpManagerPhone headsUpManagerPhone,
+ DynamicPrivacyController dynamicPrivacyController,
+ BypassHeadsUpNotifier bypassHeadsUpNotifier,
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
+ Lazy<NewNotifPipeline> newNotifPipeline,
+ FalsingManager falsingManager,
+ BroadcastDispatcher broadcastDispatcher,
+ RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ NotificationGutsManager notificationGutsManager,
+ NotificationLogger notificationLogger,
+ NotificationEntryManager notificationEntryManager,
+ NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationViewHierarchyManager notificationViewHierarchyManager,
+ ForegroundServiceController foregroundServiceController,
+ AppOpsController appOpsController,
+ KeyguardViewMediator keyguardViewMediator,
+ ZenModeController zenModeController,
+ NotificationAlertingManager notificationAlertingManager,
+ DisplayMetrics displayMetrics,
+ MetricsLogger metricsLogger,
+ UiOffloadThread uiOffloadThread,
+ NotificationMediaManager notificationMediaManager,
+ NotificationLockscreenUserManager lockScreenUserManager,
+ NotificationRemoteInputManager remoteInputManager,
+ UserSwitcherController userSwitcherController,
+ NetworkController networkController,
+ BatteryController batteryController,
+ SysuiColorExtractor colorExtractor,
+ ScreenLifecycle screenLifecycle,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ SysuiStatusBarStateController statusBarStateController,
+ VibratorHelper vibratorHelper,
+ BubbleController bubbleController,
+ NotificationGroupManager groupManager,
+ NotificationGroupAlertTransferHelper groupAlertTransferHelper,
+ VisualStabilityManager visualStabilityManager,
+ DeviceProvisionedController deviceProvisionedController,
+ NavigationBarController navigationBarController,
+ AssistManager assistManager,
+ NotificationListener notificationListener,
+ ConfigurationController configurationController,
+ StatusBarWindowController statusBarWindowController,
+ StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder,
+ NotifLog notifLog,
+ DozeParameters dozeParameters,
+ ScrimController scrimController,
+ Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ DozeScrimController dozeScrimController,
+ CarNavigationBarController carNavigationBarController) {
+ return new CarStatusBar(
+ context,
+ featureFlags,
+ lightBarController,
+ autoHideController,
+ keyguardUpdateMonitor,
+ statusBarIconController,
+ dozeLog,
+ injectionInflationController,
+ pulseExpansionHandler,
+ notificationWakeUpCoordinator,
+ keyguardBypassController,
+ keyguardStateController,
+ headsUpManagerPhone,
+ dynamicPrivacyController,
+ bypassHeadsUpNotifier,
+ allowNotificationLongPress,
+ newNotifPipeline,
+ falsingManager,
+ broadcastDispatcher,
+ remoteInputQuickSettingsDisabler,
+ notificationGutsManager,
+ notificationLogger,
+ notificationEntryManager,
+ notificationInterruptionStateProvider,
+ notificationViewHierarchyManager,
+ foregroundServiceController,
+ appOpsController,
+ keyguardViewMediator,
+ zenModeController,
+ notificationAlertingManager,
+ displayMetrics,
+ metricsLogger,
+ uiOffloadThread,
+ notificationMediaManager,
+ lockScreenUserManager,
+ remoteInputManager,
+ userSwitcherController,
+ networkController,
+ batteryController,
+ colorExtractor,
+ screenLifecycle,
+ wakefulnessLifecycle,
+ statusBarStateController,
+ vibratorHelper,
+ bubbleController,
+ groupManager,
+ groupAlertTransferHelper,
+ visualStabilityManager,
+ deviceProvisionedController,
+ navigationBarController,
+ assistManager,
+ notificationListener,
+ configurationController,
+ statusBarWindowController,
+ statusBarWindowViewControllerBuilder,
+ notifLog,
+ dozeParameters,
+ scrimController,
+ lockscreenWallpaperLazy,
+ biometricUnlockControllerLazy,
+ dozeServiceHost,
+ powerManager,
+ dozeScrimController,
+ carNavigationBarController);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 93e553f..d3a6cde 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -46,8 +46,6 @@
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
@Module
abstract class CarSystemUIModule {
@@ -105,11 +103,6 @@
public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
@Binds
- @IntoMap
- @ClassKey(StatusBar.class)
- public abstract SystemUI providesStatusBar(CarStatusBar statusBar);
-
- @Binds
abstract VolumeDialogComponent bindVolumeDialogComponent(
CarVolumeDialogComponent carVolumeDialogComponent);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index c2847c8..51b263e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -29,6 +29,7 @@
@Singleton
@Component(
modules = {
+ CarComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
SystemUIFactory.ContextHolder.class,
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index ce763b9..85472ff 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -27,7 +27,6 @@
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.car.trust.CarTrustAgentEnrollmentManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -135,16 +134,13 @@
import java.io.PrintWriter;
import java.util.Map;
-import javax.inject.Inject;
import javax.inject.Named;
-import javax.inject.Singleton;
import dagger.Lazy;
/**
* A status bar tailored for the automotive use case.
*/
-@Singleton
public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
// used to calculate how fast to open or close the window
@@ -221,6 +217,8 @@
// Whether heads-up notifications should be shown when shade is open.
private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
+
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
// When the car powers on, clear all notifications and mute/unread states.
@@ -235,7 +233,6 @@
}
};
- @Inject
public CarStatusBar(
Context context,
FeatureFlags featureFlags,
@@ -428,14 +425,16 @@
}
});
+ // Used by onDrivingStateChanged and it can be called inside
+ // DrivingStateHelper.connectToCarService()
+ mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
+
// Register a listener for driving state changes.
mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
mDrivingStateHelper.connectToCarService();
mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
mPowerManagerHelper.connectToCarService();
-
- mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
}
private void resetSystemBarsIfNecessary() {
@@ -572,13 +571,22 @@
animateCollapsePanels();
}
});
- Car car = Car.createCar(mContext);
- CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
- car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
CarNotificationListener carNotificationListener = new CarNotificationListener();
- CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
- new CarUxRestrictionManagerWrapper();
- carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
+ mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();
+ // This can take time if car service is not ready up to this time.
+ // TODO(b/142808072) Refactor CarUxRestrictionManagerWrapper to allow setting
+ // CarUxRestrictionsManager later and switch to Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT.
+ Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+ (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ CarUxRestrictionsManager carUxRestrictionsManager =
+ (CarUxRestrictionsManager)
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+ carUxRestrictionsManager);
+ });
mNotificationDataManager = new NotificationDataManager();
mNotificationDataManager.setOnUnseenCountUpdateListener(
@@ -598,7 +606,7 @@
mNotificationClickHandlerFactory, mNotificationDataManager);
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
- carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+ carNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
carHeadsUpNotificationManager, mNotificationDataManager);
mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
@@ -698,7 +706,7 @@
mNotificationView,
PreprocessingManager.getInstance(mContext),
carNotificationListener,
- carUxRestrictionManagerWrapper,
+ mCarUxRestrictionManagerWrapper,
mNotificationDataManager);
mNotificationViewController.enable();
}
@@ -947,12 +955,8 @@
UserSwitcherController userSwitcherController =
Dependency.get(UserSwitcherController.class);
if (userSwitcherController.useFullscreenUserSwitcher()) {
- Car car = Car.createCar(mContext);
- CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
- .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
- mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
- enrollmentManager, mContext);
+ mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
} else {
super.createUserSwitcher();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index a442426..cd87e78 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -17,14 +17,11 @@
package com.android.systemui.statusbar.car;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -55,16 +52,11 @@
if (mDrivingStateManager == null) {
return false;
}
- try {
- CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
- if (currentState != null) {
- return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
- || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Cannot determine current driving state. Car not connected", e);
+ CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
+ if (currentState != null) {
+ return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
+ || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
}
-
return false; // Default to false.
}
@@ -72,55 +64,25 @@
* Establishes connection with the Car service.
*/
public void connectToCarService() {
- mCar = Car.createCar(mContext, mCarConnectionListener);
- if (mCar != null) {
- mCar.connect();
- }
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
- /**
- * Disconnects from Car service and cleans up listeners.
- */
- public void disconnectFromCarService() {
- if (mCar != null) {
- mCar.disconnect();
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
}
- }
-
- private final ServiceConnection mCarConnectionListener =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- logD("Car Service connected");
- try {
- mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
- Car.CAR_DRIVING_STATE_SERVICE);
- if (mDrivingStateManager != null) {
- mDrivingStateManager.registerListener(mDrivingStateHandler);
- mDrivingStateHandler.onDrivingStateChanged(
- mDrivingStateManager.getCurrentCarDrivingState());
- } else {
- Log.e(TAG, "CarDrivingStateService service not available");
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyDrivingStateManager();
- }
- };
-
- private void destroyDrivingStateManager() {
- try {
- if (mDrivingStateManager != null) {
- mDrivingStateManager.unregisterListener();
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Error unregistering listeners", e);
+ logD("Car Service connected");
+ mDrivingStateManager = (CarDrivingStateManager) car.getCarManager(
+ Car.CAR_DRIVING_STATE_SERVICE);
+ if (mDrivingStateManager != null) {
+ mDrivingStateManager.registerListener(mDrivingStateHandler);
+ mDrivingStateHandler.onDrivingStateChanged(
+ mDrivingStateManager.getCurrentCarDrivingState());
+ } else {
+ Log.e(TAG, "CarDrivingStateService service not available");
}
- }
+ };
private void logD(String message) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 0f7c1ee..31aced0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.car.Car;
import android.car.trust.CarTrustAgentEnrollmentManager;
import android.car.userlib.CarUserManagerHelper;
import android.content.BroadcastReceiver;
@@ -50,7 +51,7 @@
private final CarStatusBar mStatusBar;
private final Context mContext;
private final UserManager mUserManager;
- private final CarTrustAgentEnrollmentManager mEnrollmentManager;
+ private CarTrustAgentEnrollmentManager mEnrollmentManager;
private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
private UserGridRecyclerView.UserRecord mSelectedUser;
private CarUserManagerHelper mCarUserManagerHelper;
@@ -64,13 +65,11 @@
mContext.unregisterReceiver(mUserUnlockReceiver);
}
};
+ private final Car mCar;
-
- public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
- CarTrustAgentEnrollmentManager enrollmentManager, Context context) {
+ public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- mEnrollmentManager = enrollmentManager;
mContext = context;
View container = mParent.findViewById(R.id.container);
@@ -86,6 +85,15 @@
mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
mUserManager = mContext.getSystemService(UserManager.class);
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mEnrollmentManager = (CarTrustAgentEnrollmentManager) car
+ .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
+ });
+
mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
@@ -201,6 +209,9 @@
}
private boolean hasTrustedDevice(int uid) {
+ if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
+ return false;
+ }
return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
index 8de1439..a27dd34 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
@@ -18,13 +18,10 @@
import android.annotation.NonNull;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
import android.util.Log;
/**
@@ -39,55 +36,30 @@
private Car mCar;
private CarPowerManager mCarPowerManager;
- private final ServiceConnection mCarConnectionListener =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Car Service connected");
- try {
- mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
- if (mCarPowerManager != null) {
- mCarPowerManager.setListener(mCarPowerStateListener);
- } else {
- Log.e(TAG, "CarPowerManager service not available");
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyCarPowerManager();
- }
- };
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener;
PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) {
mContext = context;
mCarPowerStateListener = listener;
+ mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ Log.d(TAG, "Car Service connected");
+ mCarPowerManager = (CarPowerManager) car.getCarManager(Car.POWER_SERVICE);
+ if (mCarPowerManager != null) {
+ mCarPowerManager.setListener(mCarPowerStateListener);
+ } else {
+ Log.e(TAG, "CarPowerManager service not available");
+ }
+ };
}
/**
* Connect to Car service.
*/
void connectToCarService() {
- mCar = Car.createCar(mContext, mCarConnectionListener);
- if (mCar != null) {
- mCar.connect();
- }
- }
-
- /**
- * Disconnects from Car service.
- */
- void disconnectFromCarService() {
- if (mCar != null) {
- mCar.disconnect();
- }
- }
-
- private void destroyCarPowerManager() {
- if (mCarPowerManager != null) {
- mCarPowerManager.clearListener();
- }
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 05657ff..fb1870a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -19,6 +19,7 @@
import static android.content.DialogInterface.BUTTON_NEGATIVE;
import static android.content.DialogInterface.BUTTON_POSITIVE;
import static android.os.UserManager.DISALLOW_ADD_USER;
+import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -123,10 +124,12 @@
}
private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
+ int fgUserId = ActivityManager.getCurrentUser();
+ UserHandle fgUserHandle = UserHandle.of(fgUserId);
List<UserRecord> userRecords = new ArrayList<>();
// If the foreground user CANNOT switch to other users, only display the foreground user.
- if (!mCarUserManagerHelper.canForegroundUserSwitchUsers()) {
+ if (mUserManager.getUserSwitchability(fgUserHandle) != SWITCHABILITY_STATUS_OK) {
userRecords.add(createForegroundUserRecord());
return userRecords;
}
@@ -137,7 +140,7 @@
continue;
}
- boolean isForeground = ActivityManager.getCurrentUser() == userInfo.id;
+ boolean isForeground = fgUserId == userInfo.id;
UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
false /* isAddUser */, isForeground);
userRecords.add(record);
@@ -147,7 +150,6 @@
userRecords.add(createStartGuestUserRecord());
// Add add user record if the foreground user can add users
- UserHandle fgUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
if (!mUserManager.hasUserRestriction(DISALLOW_ADD_USER, fgUserHandle)) {
userRecords.add(createAddUserRecord());
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index e81be1b..41914d2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -20,15 +20,13 @@
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.VehicleUnit;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
import android.os.Handler;
-import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
@@ -54,6 +52,7 @@
private Car mCar;
private CarHvacManager mHvacManager;
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
+
/**
* Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
* match.
@@ -85,39 +84,17 @@
+ " zone: " + zone);
}
};
- /**
- * If the connection to car service goes away then restart it.
- */
- private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- Log.d(TAG, "Death of HVAC triggering a restart");
- if (mCar != null) {
- mCar.disconnect();
- }
- destroyHvacManager();
- mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
- }
- };
- /**
- * Registers callbacks and initializes components upon connection.
- */
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- service.linkToDeath(mRestart, 0);
- mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
- mHvacManager.registerCallback(mHardwareCallback);
- initComponents();
- } catch (Exception e) {
- Log.e(TAG, "Failed to correctly connect to HVAC", e);
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyHvacManager();
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ try {
+ mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE);
+ mHvacManager.registerCallback(mHardwareCallback);
+ initComponents();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to correctly connect to HVAC", e);
}
};
@@ -132,18 +109,8 @@
*/
public void connectToCarService() {
mHandler = new Handler();
- mCar = Car.createCar(mContext, mServiceConnection, mHandler);
- if (mCar != null) {
- // note: this connect call handles the retries
- mCar.connect();
- }
- }
-
- private void destroyHvacManager() {
- if (mHvacManager != null) {
- mHvacManager.unregisterCallback(mHardwareCallback);
- mHvacManager = null;
- }
+ mCar = Car.createCar(mContext, /* handler= */ mHandler, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 22c7c7a..d979bad 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -24,12 +24,10 @@
import android.app.Dialog;
import android.app.KeyguardManager;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.media.CarAudioManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.ServiceConnection;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Color;
@@ -39,7 +37,6 @@
import android.media.AudioManager;
import android.os.Debug;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
@@ -146,42 +143,30 @@
private boolean mDismissing;
private boolean mExpanded;
private View mExpandIcon;
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mExpanded = false;
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
- // Populates volume slider items from volume groups to UI.
- for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
- VolumeItem volumeItem = getVolumeItemForUsages(
- mCarAudioManager.getUsagesForVolumeGroupId(groupId));
- mAvailableVolumeItems.add(volumeItem);
- // The first one is the default item.
- if (groupId == 0) {
- setuptListItem(0);
- }
- }
- // If list is already initiated, update its content.
- if (mVolumeItemsAdapter != null) {
- mVolumeItemsAdapter.notifyDataSetChanged();
- }
- mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mExpanded = false;
+ mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
+ int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+ // Populates volume slider items from volume groups to UI.
+ for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+ VolumeItem volumeItem = getVolumeItemForUsages(
+ mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+ mAvailableVolumeItems.add(volumeItem);
+ // The first one is the default item.
+ if (groupId == 0) {
+ setuptListItem(0);
}
}
- /**
- * This does not get called when service is properly disconnected.
- * So we need to also handle cleanups in destroy().
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- cleanupAudioManager();
+ // If list is already initiated, update its content.
+ if (mVolumeItemsAdapter != null) {
+ mVolumeItemsAdapter.notifyDataSetChanged();
}
+ mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
};
private void setuptListItem(int groupId) {
@@ -196,25 +181,14 @@
public CarVolumeDialogImpl(Context context) {
mContext = context;
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mCar = Car.createCar(mContext, mServiceConnection);
}
private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
+ return carAudioManager.getGroupVolume(volumeGroupId);
}
private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupMaxVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
+ return carAudioManager.getGroupMaxVolume(volumeGroupId);
}
/**
@@ -224,8 +198,8 @@
@Override
public void init(int windowType, Callback callback) {
initDialog();
-
- mCar.connect();
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
@Override
@@ -235,7 +209,10 @@
cleanupAudioManager();
// unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
// audio manager beforehand.
- mCar.disconnect();
+ if (mCar != null) {
+ mCar.disconnect();
+ mCar = null;
+ }
}
private void initDialog() {
@@ -605,18 +582,14 @@
// sent back down again.
return;
}
- try {
- if (mCarAudioManager == null) {
- Log.w(TAG, "Ignoring volume change event because the car isn't connected");
- return;
- }
- mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
- mAvailableVolumeItems.get(
- mVolumeGroupId).carVolumeItem.setProgress(progress);
- mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+ return;
}
+ mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+ mAvailableVolumeItems.get(
+ mVolumeGroupId).carVolumeItem.setProgress(progress);
+ mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
}
@Override
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 48d34ae..af96982 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -129,17 +129,17 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
enforceShellRestrictions();
- return super.enforceReadPermissionInner(uri, callingPkg, callerToken);
+ return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
enforceShellRestrictions();
- return super.enforceWritePermissionInner(uri, callingPkg, callerToken);
+ return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
}
public void updateVolumes() {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 0a37cc6..99f6a92 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -16,7 +16,6 @@
package com.android.packageinstaller.handheld;
-import static android.os.storage.StorageManager.convert;
import static android.text.format.Formatter.formatFileSize;
import android.annotation.NonNull;
@@ -34,8 +33,6 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -64,29 +61,18 @@
* @return The number of bytes.
*/
private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) {
- StorageManager storageManager = getContext().getSystemService(StorageManager.class);
StorageStatsManager storageStatsManager =
getContext().getSystemService(StorageStatsManager.class);
-
- List<StorageVolume> volumes = storageManager.getStorageVolumes();
- long appDataSize = 0;
-
- int numVolumes = volumes.size();
- for (int i = 0; i < numVolumes; i++) {
- StorageStats stats;
- try {
- stats = storageStatsManager.queryStatsForPackage(convert(volumes.get(i).getUuid()),
- pkg, user);
- } catch (PackageManager.NameNotFoundException | IOException e) {
- Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg + " on "
- + volumes.get(i) + " (user " + user + ")", e);
- continue;
- }
-
- appDataSize += stats.getDataBytes();
+ try {
+ StorageStats stats = storageStatsManager.queryStatsForPackage(
+ getContext().getPackageManager().getApplicationInfo(pkg, 0).storageUuid,
+ pkg, user);
+ return stats.getDataBytes();
+ } catch (PackageManager.NameNotFoundException | IOException e) {
+ Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg, e);
}
- return appDataSize;
+ return 0;
}
/**
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index c4df2e8..b9daf7f 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -406,8 +406,8 @@
return null;
}
try {
- return provider.call(context.getPackageName(), uri.getAuthority(),
- method, uri.toString(), bundle);
+ return provider.call(context.getPackageName(), context.getFeatureId(),
+ uri.getAuthority(), method, uri.toString(), bundle);
} catch (RemoteException e) {
return null;
}
diff --git a/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java b/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java
index e68b0d1..8b17ddf 100644
--- a/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java
+++ b/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java
@@ -56,6 +56,16 @@
List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled);
/**
+ * Return a list of dynamic raw data for indexing. See {@link SearchIndexableRaw}
+ *
+ * @param context the context.
+ * @param enabled hint telling if the data needs to be considered into the search results
+ * or not.
+ * @return a list of {@link SearchIndexableRaw} references. Can be null.
+ */
+ List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled);
+
+ /**
* Return a list of data keys that cannot be indexed. See {@link SearchIndexableRaw}
*
* @param context the context.
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 4e052f1..69f1c17 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -18,6 +18,7 @@
import android.os.Handler;
import android.os.Looper;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -64,11 +65,16 @@
* @Return A future of the task that can be monitored for updates or cancelled.
*/
public static Future postOnBackgroundThread(Runnable runnable) {
- if (sThreadExecutor == null) {
- sThreadExecutor = Executors.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors());
- }
- return sThreadExecutor.submit(runnable);
+ return getThreadExecutor().submit(runnable);
+ }
+
+ /**
+ * Posts callable in background using shared background thread pool.
+ *
+ * @Return A future of the task that can be monitored for updates or cancelled.
+ */
+ public static Future postOnBackgroundThread(Callable callable) {
+ return getThreadExecutor().submit(callable);
}
/**
@@ -78,4 +84,11 @@
getUiThreadHandler().post(runnable);
}
+ private static synchronized ExecutorService getThreadExecutor() {
+ if (sThreadExecutor == null) {
+ sThreadExecutor = Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors());
+ }
+ return sThreadExecutor;
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index f7fc0c5..0c3254a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -228,5 +228,6 @@
VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 8fb879d..1e75fe7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -248,7 +248,7 @@
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
} catch (RemoteException e) {
@@ -264,7 +264,7 @@
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
Settings.CALL_METHOD_LIST_CONFIG, null, args);
if (b != null) {
Map<String, String> flagsToValues =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index fdc987f..7765935 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2115,10 +2115,7 @@
}
private static String getSettingPrefix(Bundle args) {
- String prefix = (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
- // Append '/' to ensure we only match properties with this exact prefix.
- // i.e. "foo" should match "foo/property" but not "foobar/property"
- return prefix != null ? prefix + "/" : null;
+ return (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
}
private static boolean getSettingMakeDefault(Bundle args) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 36360a3..3b3ca5b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -309,7 +309,7 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callListCommand, null, arg);
lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
Collections.sort(lines);
@@ -334,7 +334,7 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callGetCommand, key, arg);
if (b != null) {
result = b.getPairValue();
@@ -372,7 +372,7 @@
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callPutCommand, key, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
@@ -396,7 +396,7 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callDeleteCommand, key, arg);
return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
@@ -423,7 +423,7 @@
}
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(packageName, Settings.AUTHORITY, callResetCommand, null, arg);
+ provider.call(packageName, null, Settings.AUTHORITY, callResetCommand, null, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9255c87..10d990a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -732,7 +732,8 @@
Settings.Secure.SILENCE_GESTURE,
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
- Settings.Secure.FACE_UNLOCK_RE_ENROLL);
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL,
+ Settings.Secure.TAP_GESTURE);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 54e291f..c59f342 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -180,6 +180,8 @@
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
<!-- Permission needed to invoke DynamicSystem (AOT) -->
<uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
+ <!-- Used to clean up heap dumps on boot. -->
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
@@ -209,7 +211,7 @@
<!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
<uses-permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE" />
-
+
<!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
@@ -239,6 +241,11 @@
</intent-filter>
</provider>
+ <provider android:name=".HeapDumpProvider"
+ android:authorities="com.android.shell.heapdump"
+ android:grantUriPermissions="true"
+ android:exported="true" />
+
<activity
android:name=".BugreportWarningActivity"
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
@@ -246,6 +253,14 @@
android:excludeFromRecents="true"
android:exported="false" />
+ <activity android:name=".HeapDumpActivity"
+ android:theme="@*android:style/Theme.Translucent.NoTitleBar"
+ android:label="@*android:string/dump_heap_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:exported="false" />
+
<receiver
android:name=".BugreportRequestedReceiver"
android:permission="android.permission.TRIGGER_SHELL_BUGREPORT">
@@ -254,6 +269,16 @@
</intent-filter>
</receiver>
+ <receiver
+ android:name=".HeapDumpReceiver"
+ android:permission="android.permission.DUMP">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="com.android.internal.intent.action.HEAP_DUMP_FINISHED" />
+ <action android:name="com.android.shell.action.DELETE_HEAP_DUMP" />
+ </intent-filter>
+ </receiver>
+
<service
android:name=".BugreportProgressService"
android:exported="false"/>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1b35770..30ad9c5 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1560,7 +1560,7 @@
return false;
}
- private static boolean isTv(Context context) {
+ static boolean isTv(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
diff --git a/packages/Shell/src/com/android/shell/HeapDumpActivity.java b/packages/Shell/src/com/android/shell/HeapDumpActivity.java
new file mode 100644
index 0000000..0ff0d33
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/HeapDumpActivity.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.shell;
+
+import static com.android.shell.HeapDumpProvider.makeUri;
+import static com.android.shell.HeapDumpReceiver.ACTION_DELETE_HEAP_DUMP;
+import static com.android.shell.HeapDumpReceiver.EXTRA_IS_USER_INITIATED;
+import static com.android.shell.HeapDumpReceiver.EXTRA_PROCESS_NAME;
+import static com.android.shell.HeapDumpReceiver.EXTRA_REPORT_PACKAGE;
+import static com.android.shell.HeapDumpReceiver.EXTRA_SIZE_BYTES;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.DebugUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+
+/**
+ * This activity is displayed when the system has collected a heap dump.
+ */
+public class HeapDumpActivity extends Activity {
+ private static final String TAG = "HeapDumpActivity";
+
+ static final String KEY_URI = "uri";
+
+ private AlertDialog mDialog;
+ private Uri mDumpUri;
+ private boolean mHandled = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String process = getIntent().getStringExtra(EXTRA_PROCESS_NAME);
+ long size = getIntent().getLongExtra(EXTRA_SIZE_BYTES, 0);
+ final boolean isUserInitiated = getIntent().getBooleanExtra(EXTRA_IS_USER_INITIATED, false);
+ final int uid = getIntent().getIntExtra(Intent.EXTRA_UID, 0);
+ final boolean isSystemProcess = uid == Process.SYSTEM_UID;
+ mDumpUri = makeUri(process);
+ final String procDisplayName = isSystemProcess
+ ? getString(com.android.internal.R.string.android_system_label)
+ : process;
+
+ final Intent sendIntent = new Intent();
+ ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", mDumpUri);
+ sendIntent.setClipData(clip);
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ sendIntent.setType(clip.getDescription().getMimeType(0));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, mDumpUri);
+
+ String directLaunchPackage = getIntent().getStringExtra(EXTRA_REPORT_PACKAGE);
+ if (directLaunchPackage != null) {
+ sendIntent.setAction(ActivityManager.ACTION_REPORT_HEAP_LIMIT);
+ sendIntent.setPackage(directLaunchPackage);
+ try {
+ startActivity(sendIntent);
+ mHandled = true;
+ finish();
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Unable to direct launch to " + directLaunchPackage, e);
+ }
+ }
+
+ final int messageId;
+ if (isUserInitiated) {
+ messageId = com.android.internal.R.string.dump_heap_ready_text;
+ } else if (isSystemProcess) {
+ messageId = com.android.internal.R.string.dump_heap_system_text;
+ } else {
+ messageId = com.android.internal.R.string.dump_heap_text;
+ }
+ mDialog = new AlertDialog.Builder(this, android.R.style.Theme_Material_Light_Dialog_Alert)
+ .setTitle(com.android.internal.R.string.dump_heap_title)
+ .setMessage(getString(messageId, procDisplayName,
+ DebugUtils.sizeValueToString(size, null)))
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
+ mHandled = true;
+ finish();
+ })
+ .setNeutralButton(R.string.delete, (dialog, which) -> {
+ mHandled = true;
+ Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP);
+ deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class);
+ deleteIntent.putExtra(KEY_URI, mDumpUri.toString());
+ sendBroadcast(deleteIntent);
+ finish();
+ })
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ mHandled = true;
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setPackage(null);
+ startActivity(Intent.createChooser(sendIntent,
+ getText(com.android.internal.R.string.dump_heap_title)));
+ finish();
+ })
+ .show();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (!isChangingConfigurations()) {
+ if (!mHandled) {
+ Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP);
+ deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class);
+ deleteIntent.putExtra(KEY_URI, mDumpUri.toString());
+ sendBroadcast(deleteIntent);
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ }
+}
diff --git a/packages/Shell/src/com/android/shell/HeapDumpProvider.java b/packages/Shell/src/com/android/shell/HeapDumpProvider.java
new file mode 100644
index 0000000..3eceb91
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/HeapDumpProvider.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.shell;
+
+import android.annotation.NonNull;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/** ContentProvider to write and access heap dumps. */
+public class HeapDumpProvider extends ContentProvider {
+ private static final String FILENAME_SUFFIX = "_javaheap.bin";
+ private static final Object sLock = new Object();
+
+ private File mRoot;
+
+ @Override
+ public boolean onCreate() {
+ synchronized (sLock) {
+ mRoot = new File(getContext().createCredentialProtectedStorageContext().getFilesDir(),
+ "heapdumps");
+ return mRoot.mkdir();
+ }
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "application/octet-stream";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("Insert not allowed.");
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ String path = sanitizePath(uri.getEncodedPath());
+ String tag = Uri.decode(path);
+ return (new File(mRoot, tag)).delete() ? 1 : 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update not allowed.");
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ String path = sanitizePath(uri.getEncodedPath());
+ String tag = Uri.decode(path);
+ final int pMode;
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ pMode = ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE
+ | ParcelFileDescriptor.MODE_WRITE_ONLY;
+ } else {
+ pMode = ParcelFileDescriptor.MODE_READ_ONLY;
+ }
+
+ synchronized (sLock) {
+ return ParcelFileDescriptor.open(new File(mRoot, tag), pMode);
+ }
+ }
+
+ @NonNull
+ static Uri makeUri(@NonNull String procName) {
+ return Uri.parse("content://com.android.shell.heapdump/" + procName + FILENAME_SUFFIX);
+ }
+
+ private String sanitizePath(String path) {
+ return path.replaceAll("[^a-zA-Z0-9_.]", "");
+ }
+}
diff --git a/packages/Shell/src/com/android/shell/HeapDumpReceiver.java b/packages/Shell/src/com/android/shell/HeapDumpReceiver.java
new file mode 100644
index 0000000..858c521
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/HeapDumpReceiver.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.shell;
+
+import static com.android.shell.BugreportProgressService.isTv;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.FileUtils;
+import android.os.Process;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Receiver that handles finished heap dumps.
+ */
+public class HeapDumpReceiver extends BroadcastReceiver {
+ private static final String TAG = "HeapDumpReceiver";
+
+ /**
+ * Broadcast action to determine when to delete a specific dump heap. Must include a {@link
+ * HeapDumpActivity#KEY_URI} String extra.
+ */
+ static final String ACTION_DELETE_HEAP_DUMP = "com.android.shell.action.DELETE_HEAP_DUMP";
+
+ /** Broadcast sent when heap dump collection has been completed. */
+ private static final String ACTION_HEAP_DUMP_FINISHED =
+ "com.android.internal.intent.action.HEAP_DUMP_FINISHED";
+
+ /** The process we are reporting */
+ static final String EXTRA_PROCESS_NAME = "com.android.internal.extra.heap_dump.PROCESS_NAME";
+
+ /** The size limit the process reached. */
+ static final String EXTRA_SIZE_BYTES = "com.android.internal.extra.heap_dump.SIZE_BYTES";
+
+ /** Whether the user initiated the dump or not. */
+ static final String EXTRA_IS_USER_INITIATED =
+ "com.android.internal.extra.heap_dump.IS_USER_INITIATED";
+
+ /** Optional name of package to directly launch. */
+ static final String EXTRA_REPORT_PACKAGE =
+ "com.android.internal.extra.heap_dump.REPORT_PACKAGE";
+
+ private static final String NOTIFICATION_CHANNEL_ID = "heapdumps";
+ private static final int NOTIFICATION_ID = 2019;
+
+ /**
+ * Always keep heap dumps taken in the last week.
+ */
+ private static final long MIN_KEEP_AGE_MS = DateUtils.WEEK_IN_MILLIS;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive(): " + intent);
+ final String action = intent.getAction();
+ if (action == null) {
+ Log.e(TAG, "null action received");
+ return;
+ }
+ switch (action) {
+ case Intent.ACTION_BOOT_COMPLETED:
+ cleanupOldFiles(context);
+ break;
+ case ACTION_DELETE_HEAP_DUMP:
+ deleteHeapDump(context, intent.getStringExtra(HeapDumpActivity.KEY_URI));
+ break;
+ case ACTION_HEAP_DUMP_FINISHED:
+ showDumpNotification(context, intent);
+ break;
+ }
+ }
+
+ private void cleanupOldFiles(Context context) {
+ final PendingResult result = goAsync();
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ Log.d(TAG, "Deleting from " + new File(context.getFilesDir(), "heapdumps"));
+ FileUtils.deleteOlderFiles(new File(context.getFilesDir(), "heapdumps"), 0,
+ MIN_KEEP_AGE_MS);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Couldn't delete old files", e);
+ }
+ result.finish();
+ return null;
+ }
+ }.execute();
+ }
+
+ private void deleteHeapDump(Context context, @Nullable final String uri) {
+ if (uri == null) {
+ Log.e(TAG, "null URI for delete heap dump intent");
+ return;
+ }
+ final PendingResult result = goAsync();
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ context.getContentResolver().delete(Uri.parse(uri), null, null);
+ result.finish();
+ return null;
+ }
+ }.execute();
+ }
+
+ private void showDumpNotification(Context context, Intent intent) {
+ final boolean isUserInitiated = intent.getBooleanExtra(
+ EXTRA_IS_USER_INITIATED, false);
+ final String procName = intent.getStringExtra(EXTRA_PROCESS_NAME);
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+
+ final String reportPackage = intent.getStringExtra(
+ EXTRA_REPORT_PACKAGE);
+ final long size = intent.getLongExtra(EXTRA_SIZE_BYTES, 0);
+
+ if (procName == null) {
+ Log.e(TAG, "No process name sent over");
+ return;
+ }
+
+ NotificationManager nm = NotificationManager.from(context);
+ nm.createNotificationChannel(
+ new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ "Heap dumps",
+ NotificationManager.IMPORTANCE_DEFAULT));
+
+ final int titleId = isUserInitiated
+ ? com.android.internal.R.string.dump_heap_ready_notification
+ : com.android.internal.R.string.dump_heap_notification;
+ final String procDisplayName = uid == Process.SYSTEM_UID
+ ? context.getString(com.android.internal.R.string.android_system_label)
+ : procName;
+ String text = context.getString(titleId, procDisplayName);
+
+ Intent shareIntent = new Intent();
+ shareIntent.setClassName(context, HeapDumpActivity.class.getName());
+ shareIntent.putExtra(EXTRA_PROCESS_NAME, procName);
+ shareIntent.putExtra(EXTRA_SIZE_BYTES, size);
+ shareIntent.putExtra(EXTRA_IS_USER_INITIATED, isUserInitiated);
+ shareIntent.putExtra(Intent.EXTRA_UID, uid);
+ if (reportPackage != null) {
+ shareIntent.putExtra(EXTRA_REPORT_PACKAGE, reportPackage);
+ }
+ final Notification.Builder builder = new Notification.Builder(context,
+ NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(
+ isTv(context) ? R.drawable.ic_bug_report_black_24dp
+ : com.android.internal.R.drawable.stat_sys_adb)
+ .setLocalOnly(true)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(text)
+ .setTicker(text)
+ .setAutoCancel(true)
+ .setContentText(context.getText(
+ com.android.internal.R.string.dump_heap_notification_detail))
+ .setContentIntent(PendingIntent.getActivity(context, 2, shareIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT));
+
+ Log.v(TAG, "Creating share heap dump notification");
+ NotificationManager.from(context).notify(NOTIFICATION_ID, builder.build());
+ }
+}
diff --git a/packages/SystemUI/res/layout/home_controls.xml b/packages/SystemUI/res/layout/home_controls.xml
index b9a6a48..69a0e87 100644
--- a/packages/SystemUI/res/layout/home_controls.xml
+++ b/packages/SystemUI/res/layout/home_controls.xml
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/home_controls_layout"
android:layout_width="match_parent"
- android:layout_height="125dp"
+ android:layout_height="wrap_content"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:visibility="gone"
android:padding="8dp"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
index 7d6ff3b1..69beffe 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
@@ -20,7 +20,7 @@
android:id="@+id/plugin_frame"
android:theme="@style/qs_theme"
android:layout_width="@dimen/qs_panel_width"
- android:layout_height="105dp"
+ android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/notification_side_paddings"
android:layout_marginLeft="@dimen/notification_side_paddings"
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 3f56ff0..8825f12 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -67,13 +67,11 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (args != null && args.length > 0 && args[0].equals("--config")) {
- dumpConfig(pw);
- return;
- }
-
dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
- dumpConfig(pw);
+
+ if (args == null || args.length == 0 || args[0].equals("--config")) {
+ dumpConfig(pw);
+ }
}
static void dumpServices(
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index ff4711c..98d7f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -23,6 +23,7 @@
import android.os.Looper
import android.os.Message
import android.os.UserHandle
+import android.text.TextUtils
import android.util.Log
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
@@ -55,7 +56,8 @@
* a given broadcast.
*
* Use only for IntentFilters with actions and optionally categories. It does not support,
- * permissions, schemes or data types. Cannot be used for getting sticky broadcasts.
+ * permissions, schemes, data types or data authorities.
+ * Cannot be used for getting sticky broadcasts.
*/
@Singleton
open class BroadcastDispatcher @Inject constructor (
@@ -72,11 +74,14 @@
*
* @param receiver A receiver to dispatch the [Intent]
* @param filter A filter to determine what broadcasts should be dispatched to this receiver.
- * It will only take into account actions and categories for filtering.
+ * It will only take into account actions and categories for filtering. It must
+ * have at least one action.
* @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
* main handler. Pass `null` to use the default.
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
* By default, it is the current user.
+ * @throws IllegalArgumentException if the filter has other constraints that are not actions or
+ * categories or the filter has no actions.
*/
@JvmOverloads
fun registerReceiver(
@@ -85,12 +90,23 @@
handler: Handler? = mainHandler,
user: UserHandle = context.user
) {
+ checkFilter(filter)
this.handler
.obtainMessage(MSG_ADD_RECEIVER,
ReceiverData(receiver, filter, handler ?: mainHandler, user))
.sendToTarget()
}
+ private fun checkFilter(filter: IntentFilter) {
+ val sb = StringBuilder()
+ if (filter.countActions() == 0) sb.append("Filter must contain at least one action. ")
+ if (filter.countDataAuthorities() != 0) sb.append("Filter cannot contain DataAuthorities. ")
+ if (filter.countDataPaths() != 0) sb.append("Filter cannot contain DataPaths. ")
+ if (filter.countDataSchemes() != 0) sb.append("Filter cannot contain DataSchemes. ")
+ if (filter.countDataTypes() != 0) sb.append("Filter cannot contain DataTypes. ")
+ if (!TextUtils.isEmpty(sb)) throw IllegalArgumentException(sb.toString())
+ }
+
/**
* Unregister receiver for all users.
* <br>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
index d6d1e41..37a447f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
@@ -20,6 +20,7 @@
import android.app.Service;
import com.android.systemui.SystemUI;
+import com.android.systemui.recents.RecentsImplementation;
/**
* Interface necessary to make Dagger happy. See {@link ContextComponentResolver}.
@@ -29,6 +30,9 @@
Activity resolveActivity(String className);
/** Turns a classname into an instance of the class or returns null. */
+ RecentsImplementation resolveRecents(String className);
+
+ /** Turns a classname into an instance of the class or returns null. */
Service resolveService(String className);
/** Turns a classname into an instance of the class or returns null. */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
index d7822c9..06339bd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
@@ -20,6 +20,7 @@
import android.app.Service;
import com.android.systemui.SystemUI;
+import com.android.systemui.recents.RecentsImplementation;
import java.util.Map;
@@ -35,15 +36,17 @@
private final Map<Class<?>, Provider<Activity>> mActivityCreators;
private final Map<Class<?>, Provider<Service>> mServiceCreators;
private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
+ private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
@Inject
- ContextComponentResolver(
- Map<Class<?>, Provider<Activity>> activityCreators,
+ ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
Map<Class<?>, Provider<Service>> serviceCreators,
- Map<Class<?>, Provider<SystemUI>> systemUICreators) {
+ Map<Class<?>, Provider<SystemUI>> systemUICreators,
+ Map<Class<?>, Provider<RecentsImplementation>> recentsCreators) {
mActivityCreators = activityCreators;
mServiceCreators = serviceCreators;
mSystemUICreators = systemUICreators;
+ mRecentsCreators = recentsCreators;
}
/**
@@ -55,6 +58,14 @@
}
/**
+ * Looks up the RecentsImplementation class name to see if Dagger has an instance of it.
+ */
+ @Override
+ public RecentsImplementation resolveRecents(String className) {
+ return resolve(className, mRecentsCreators);
+ }
+
+ /**
* Looks up the Service class name to see if Dagger has an instance of it.
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java
rename to packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 4be610f..61ded13 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -30,7 +30,7 @@
* Services and Activities that are injectable should go here.
*/
@Module
-public abstract class ActivityBinder {
+public abstract class DefaultActivityBinder {
/** Inject into TunerActivity. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
rename to packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
index 4e4c06e..d8989ee 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
@@ -16,16 +16,13 @@
package com.android.systemui.dagger;
-import dagger.Binds;
import dagger.Module;
/**
* Dagger Module that collects related sub-modules together.
+ *
+ * See {@link ContextComponentResolver}
*/
-@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
-public abstract class ComponentBinder {
- /** */
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
+@Module(includes = {DefaultActivityBinder.class, DefaultServiceBinder.class, SystemUIBinder.class})
+public abstract class DefaultComponentBinder {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java
rename to packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index 1f2c0a1..14bb80c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -31,7 +31,7 @@
* Services that are injectable should go here.
*/
@Module
-public abstract class ServiceBinder {
+public abstract class DefaultServiceBinder {
/** */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 738f539..c3d2a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,19 +16,89 @@
package com.android.systemui.dagger;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.util.DisplayMetrics;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.LatencyTester;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SystemUI;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.pip.PipUI;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.volume.VolumeUI;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
import dagger.Binds;
+import dagger.Lazy;
import dagger.Module;
+import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
@@ -80,10 +150,151 @@
@ClassKey(ScreenDecorations.class)
public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+ /** Inject into StatusBar. */
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBar.class)
+ public abstract SystemUI bindsStatusBar(StatusBar sysui);
+
/** Inject into VolumeUI. */
@Binds
@IntoMap
@ClassKey(VolumeUI.class)
public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+ /**
+ * Provides our instance of StatusBar which is considered optional.
+ */
+ @Provides
+ @Singleton
+ static StatusBar provideStatusBar(
+ Context context,
+ FeatureFlags featureFlags,
+ LightBarController lightBarController,
+ AutoHideController autoHideController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ StatusBarIconController statusBarIconController,
+ DozeLog dozeLog,
+ InjectionInflationController injectionInflationController,
+ PulseExpansionHandler pulseExpansionHandler,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator,
+ KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
+ HeadsUpManagerPhone headsUpManagerPhone,
+ DynamicPrivacyController dynamicPrivacyController,
+ BypassHeadsUpNotifier bypassHeadsUpNotifier,
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
+ Lazy<NewNotifPipeline> newNotifPipeline,
+ FalsingManager falsingManager,
+ BroadcastDispatcher broadcastDispatcher,
+ RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ NotificationGutsManager notificationGutsManager,
+ NotificationLogger notificationLogger,
+ NotificationEntryManager notificationEntryManager,
+ NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationViewHierarchyManager notificationViewHierarchyManager,
+ ForegroundServiceController foregroundServiceController,
+ AppOpsController appOpsController,
+ KeyguardViewMediator keyguardViewMediator,
+ ZenModeController zenModeController,
+ NotificationAlertingManager notificationAlertingManager,
+ DisplayMetrics displayMetrics,
+ MetricsLogger metricsLogger,
+ UiOffloadThread uiOffloadThread,
+ NotificationMediaManager notificationMediaManager,
+ NotificationLockscreenUserManager lockScreenUserManager,
+ NotificationRemoteInputManager remoteInputManager,
+ UserSwitcherController userSwitcherController,
+ NetworkController networkController,
+ BatteryController batteryController,
+ SysuiColorExtractor colorExtractor,
+ ScreenLifecycle screenLifecycle,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ SysuiStatusBarStateController statusBarStateController,
+ VibratorHelper vibratorHelper,
+ BubbleController bubbleController,
+ NotificationGroupManager groupManager,
+ NotificationGroupAlertTransferHelper groupAlertTransferHelper,
+ VisualStabilityManager visualStabilityManager,
+ DeviceProvisionedController deviceProvisionedController,
+ NavigationBarController navigationBarController,
+ AssistManager assistManager,
+ NotificationListener notificationListener,
+ ConfigurationController configurationController,
+ StatusBarWindowController statusBarWindowController,
+ StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder,
+ NotifLog notifLog,
+ DozeParameters dozeParameters,
+ ScrimController scrimController,
+ Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ DozeScrimController dozeScrimController) {
+ return new StatusBar(
+ context,
+ featureFlags,
+ lightBarController,
+ autoHideController,
+ keyguardUpdateMonitor,
+ statusBarIconController,
+ dozeLog,
+ injectionInflationController,
+ pulseExpansionHandler,
+ notificationWakeUpCoordinator,
+ keyguardBypassController,
+ keyguardStateController,
+ headsUpManagerPhone,
+ dynamicPrivacyController,
+ bypassHeadsUpNotifier,
+ allowNotificationLongPress,
+ newNotifPipeline,
+ falsingManager,
+ broadcastDispatcher,
+ remoteInputQuickSettingsDisabler,
+ notificationGutsManager,
+ notificationLogger,
+ notificationEntryManager,
+ notificationInterruptionStateProvider,
+ notificationViewHierarchyManager,
+ foregroundServiceController,
+ appOpsController,
+ keyguardViewMediator,
+ zenModeController,
+ notificationAlertingManager,
+ displayMetrics,
+ metricsLogger,
+ uiOffloadThread,
+ notificationMediaManager,
+ lockScreenUserManager,
+ remoteInputManager,
+ userSwitcherController,
+ networkController,
+ batteryController,
+ colorExtractor,
+ screenLifecycle,
+ wakefulnessLifecycle,
+ statusBarStateController,
+ vibratorHelper,
+ bubbleController,
+ groupManager,
+ groupAlertTransferHelper,
+ visualStabilityManager,
+ deviceProvisionedController,
+ navigationBarController,
+ assistManager,
+ notificationListener,
+ configurationController,
+ statusBarWindowController,
+ statusBarWindowViewControllerBuilder,
+ notifLog,
+ dozeParameters,
+ scrimController,
+ lockscreenWallpaperLazy,
+ biometricUnlockControllerLazy,
+ dozeServiceHost,
+ powerManager,
+ dozeScrimController);
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index c95b50b..7b8d3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -21,7 +21,6 @@
import androidx.annotation.Nullable;
-import com.android.systemui.SystemUI;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.power.EnhancedEstimates;
@@ -39,8 +38,6 @@
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
/**
* A dagger module for injecting default implementations of components of System UI that may be
@@ -74,11 +71,6 @@
@Binds
abstract ShadeController provideShadeController(StatusBar statusBar);
- @Binds
- @IntoMap
- @ClassKey(StatusBar.class)
- public abstract SystemUI providesStatusBar(StatusBar statusBar);
-
@Singleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4e60f19..5f1455f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -26,10 +26,13 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.sensors.AsyncSensorManager;
import javax.inject.Singleton;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
@@ -38,9 +41,12 @@
* implementation.
*/
@Module(includes = {AssistModule.class,
- ComponentBinder.class,
PeopleHubModule.class})
public abstract class SystemUIModule {
+ /** */
+ @Binds
+ public abstract ContextComponentHelper bindComponentHelper(
+ ContextComponentResolver componentHelper);
@Singleton
@Provides
@@ -56,10 +62,12 @@
keyguardUpdateMonitor);
}
-
@Singleton
@Provides
static SysUiState provideSysUiState() {
return new SysUiState();
}
+
+ @BindsOptionalOf
+ abstract StatusBar optionalStatusBar();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index 113c9c8..83d956c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -37,6 +37,7 @@
*/
@Singleton
@Component(modules = {
+ DefaultComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
SystemServicesModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
index 89b7a818..acf761ed 100644
--- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
@@ -67,7 +67,7 @@
private B mBuilder = getBuilder();
protected int mType = UNINITIALIZED;
protected String mReason;
- protected @Level int mLogLevel;
+ protected @Level int mLogLevel = VERBOSE;
/**
* Get the log-specific builder.
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
index a6e10e6..f094cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -120,9 +120,9 @@
}
/**
- * @return user-readable string of the given event
+ * @return user-readable string of the given event with timestamp
*/
- public String eventToString(Event event) {
+ public String eventToTimestampedString(Event event) {
StringBuilder sb = new StringBuilder();
sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
sb.append(" ");
@@ -131,13 +131,20 @@
}
/**
+ * @return user-readable string of the given event without a timestamp
+ */
+ public String eventToString(Event event) {
+ return event.getMessage();
+ }
+
+ /**
* only call on this method if you have the mDataLock
*/
private void dumpTimelineLocked(PrintWriter pw) {
pw.println("\tTimeline:");
for (Event event : mTimeline) {
- pw.println("\t" + eventToString(event));
+ pw.println("\t" + eventToTimestampedString(event));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index a267bbb..a0ea7fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,7 +14,6 @@
package com.android.systemui.qs;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -31,6 +30,7 @@
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -270,9 +270,7 @@
}
- int flag = Settings.System.getInt(mQsPanel.getContext().getContentResolver(),
- "qs_media_player", 0);
- if (flag == 1) {
+ if (Utils.useQsMediaPlayer(mQsPanel.getContext())) {
View qsMediaView = mQsPanel.getMediaPanel();
View qqsMediaView = mQuickQsPanel.getMediaPlayer().getView();
translationXBuilder.addFloat(qsMediaView, "alpha", 0, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b48814b..39f0865 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -18,6 +18,7 @@
import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -150,8 +151,7 @@
addDivider();
// Add media carousel
- int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
- if (flag == 1) {
+ if (useQsMediaPlayer(context)) {
HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext);
mediaScrollView.setHorizontalScrollBarEnabled(false);
int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
@@ -201,8 +201,7 @@
*/
public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
View actionsContainer, StatusBarNotification notif) {
- int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
- if (flag != 1) {
+ if (!useQsMediaPlayer(mContext)) {
// Shouldn't happen, but just in case
Log.e(TAG, "Tried to add media session without player!");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index dcd4633..94a1cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -36,6 +35,7 @@
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -72,8 +72,7 @@
removeView((View) mTileLayout);
}
- int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
- if (flag == 1) {
+ if (Utils.useQsMediaPlayer(context)) {
LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext);
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
mHorizontalLinearLayout.setClipChildren(false);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 592e388..16c61e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,7 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.annotation.ColorInt;
import android.app.ActivityManager;
@@ -393,11 +394,10 @@
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
if (mQsDisabled) {
lp.height = resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- } else if (flag == 1) {
+ } else if (useQsMediaPlayer(mContext)) {
lp.height = Math.max(getMinimumHeight(),
resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_total_height_with_media));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 958695d..7f11e56 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.trust.TrustManager;
import android.content.Context;
@@ -40,12 +41,22 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
/**
* An implementation of the Recents interface which proxies to the OverviewProxyService.
*/
+@Singleton
public class OverviewProxyRecentsImpl implements RecentsImplementation {
private final static String TAG = "OverviewProxyRecentsImpl";
+ @Nullable
+ private final Lazy<StatusBar> mStatusBarLazy;
private SysUiServiceProvider mSysUiServiceProvider;
private Context mContext;
@@ -53,6 +64,12 @@
private TrustManager mTrustManager;
private OverviewProxyService mOverviewProxyService;
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+ @Inject
+ public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
+ mStatusBarLazy = statusBarLazy.orElse(null);
+ }
+
@Override
public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {
mContext = context;
@@ -107,9 +124,8 @@
}
};
// Preload only if device for current user is unlocked
- final StatusBar statusBar = mSysUiServiceProvider.getComponent(StatusBar.class);
- if (statusBar != null && statusBar.isKeyguardShowing()) {
- statusBar.executeRunnableDismissingKeyguard(() -> {
+ if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
+ mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
// Flush trustmanager before checking device locked per user
mTrustManager.reportKeyguardShowingChanged();
mHandler.post(toggleRecents);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index 3efed3f..8cd17e9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -23,7 +23,10 @@
import java.io.PrintWriter;
-interface RecentsImplementation {
+/**
+ * API for creating a Recents view.
+ */
+public interface RecentsImplementation {
default void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {}
default void onBootCompleted() {}
default void onAppTransitionFinished() {}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
index 5555285..f57bfad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
@@ -19,35 +19,53 @@
import android.content.Context;
import com.android.systemui.R;
+import com.android.systemui.dagger.ContextComponentHelper;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
/**
* Dagger injection module for {@link RecentsImplementation}
*/
@Module
-public class RecentsModule {
+public abstract class RecentsModule {
+
/**
* @return The {@link RecentsImplementation} from the config.
*/
@Provides
- public RecentsImplementation provideRecentsImpl(Context context) {
+ public static RecentsImplementation provideRecentsImpl(Context context,
+ ContextComponentHelper componentHelper) {
final String clsName = context.getString(R.string.config_recentsComponent);
if (clsName == null || clsName.length() == 0) {
throw new RuntimeException("No recents component configured", null);
}
- Class<?> cls = null;
- try {
- cls = context.getClassLoader().loadClass(clsName);
- } catch (Throwable t) {
- throw new RuntimeException("Error loading recents component: " + clsName, t);
+ RecentsImplementation impl = componentHelper.resolveRecents(clsName);
+
+ if (impl == null) {
+ Class<?> cls = null;
+ try {
+ cls = context.getClassLoader().loadClass(clsName);
+ } catch (Throwable t) {
+ throw new RuntimeException("Error loading recents component: " + clsName, t);
+ }
+ try {
+ impl = (RecentsImplementation) cls.newInstance();
+ } catch (Throwable t) {
+ throw new RuntimeException("Error creating recents component: " + clsName, t);
+ }
}
- try {
- RecentsImplementation impl = (RecentsImplementation) cls.newInstance();
- return impl;
- } catch (Throwable t) {
- throw new RuntimeException("Error creating recents component: " + clsName, t);
- }
+
+ return impl;
}
+
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(OverviewProxyRecentsImpl.class)
+ public abstract RecentsImplementation bindOverviewProxyRecentsImpl(
+ OverviewProxyRecentsImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index d6b87af..20a3e35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -21,7 +21,6 @@
import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -40,6 +39,7 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -146,9 +146,7 @@
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
- int flag = Settings.System.getInt(mContext.getContentResolver(),
- "qs_media_player", 0);
- boolean hideMedia = (flag == 1);
+ boolean hideMedia = Utils.useQsMediaPlayer(mContext);
if (ent.isRowDismissed() || ent.isRowRemoved()
|| (ent.isMediaNotification() && hideMedia)
|| mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 9bc0ca4..d0122c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -28,7 +28,6 @@
import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.os.Handler;
-import android.provider.Settings;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -48,6 +47,7 @@
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.util.Utils;
import java.util.Timer;
import java.util.TimerTask;
@@ -182,8 +182,7 @@
final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
- int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
- if (flag == 1) {
+ if (Utils.useQsMediaPlayer(mContext)) {
StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_qs_panel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index e00cfb1..467df37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -109,6 +109,7 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.Utils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -769,8 +770,7 @@
int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
int topMargin =
res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
- int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
- if (flag == 1) {
+ if (Utils.useQsMediaPlayer(mContext)) {
topMargin = res.getDimensionPixelOffset(
com.android.internal.R.dimen.quick_qs_total_height_with_media);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index afc147a..001599f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -156,6 +156,7 @@
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.SystemUIBinder;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.ExtensionFragmentListener;
@@ -246,14 +247,11 @@
import java.util.ArrayList;
import java.util.Map;
-import javax.inject.Inject;
import javax.inject.Named;
-import javax.inject.Singleton;
import dagger.Lazy;
import dagger.Subcomponent;
-@Singleton
public class StatusBar extends SystemUI implements DemoMode,
ActivityStarter, KeyguardStateController.Callback,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
@@ -626,7 +624,12 @@
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION};
- @Inject
+ /**
+ * Public constructor for StatusBar.
+ *
+ * StatusBar is considered optional, and therefore can not be marked as @Inject directly.
+ * Instead, an @Provide method is included in {@link SystemUIBinder}.
+ */
public StatusBar(
Context context,
FeatureFlags featureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 92a8d84..aa9c5ac 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.view.View;
import com.android.systemui.SysUiServiceProvider;
@@ -124,4 +125,13 @@
&& QuickStepContract.isGesturalMode(navMode);
}
+ /**
+ * Allow the media player to be shown in the QS area, controlled by 2 flags.
+ */
+ public static boolean useQsMediaPlayer(Context context) {
+ int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
+ flag |= Settings.System.getInt(context.getContentResolver(), "npv_plugin_flag", 0);
+
+ return flag > 0;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 2bff548..ead14e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -21,6 +21,7 @@
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
+import android.os.PatternMatcher
import android.os.UserHandle
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
@@ -33,6 +34,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -48,6 +50,10 @@
val user1 = UserHandle.of(1)
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ const val TEST_ACTION = "TEST_ACTION"
+ const val TEST_SCHEME = "TEST_SCHEME"
+ const val TEST_PATH = "TEST_PATH"
+ const val TEST_TYPE = "test/type"
}
@Mock
@@ -83,6 +89,11 @@
Handler(testableLooper.looper),
testableLooper.looper,
mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
+
+ // These should be valid filters
+ `when`(intentFilter.countActions()).thenReturn(1)
+ `when`(intentFilterOther.countActions()).thenReturn(1)
+ `when`(mockContext.user).thenReturn(user0)
}
@Test
@@ -129,6 +140,44 @@
verify(mockUBRUser1, never()).unregisterReceiver(broadcastReceiver)
}
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustContainActions() {
+ val testFilter = IntentFilter()
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataScheme() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataScheme(TEST_SCHEME)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataAuthority() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java))
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataPath() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataType() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataType(TEST_TYPE)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
private class TestBroadcastDispatcher(
context: Context,
mainHandler: Handler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index a1822c7..f5d6f22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -50,19 +50,14 @@
fun testBindViewModelToViewBoundary() {
val fakePerson1 = fakePersonViewModel("name")
val fakeViewModel = PeopleHubViewModel(sequenceOf(fakePerson1), true)
-
val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>()
val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>()
-
val mockClickView = mock(View::class.java)
-
`when`(mockViewBoundary.associatedViewForClickAnimation).thenReturn(mockClickView)
`when`(mockViewBoundary.personViewAdapters)
.thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2))
-
val mockFactory = mock(PeopleHubViewModelFactory::class.java)
`when`(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel)
-
val mockSubscription = mock(Subscription::class.java)
val fakeFactoryDataSource = object : DataSource<PeopleHubViewModelFactory> {
override fun registerListener(
@@ -82,6 +77,7 @@
verify(mockFactory).createWithAssociatedClickView(mockClickView)
}
+ @Test
fun testViewModelDataSourceTransformsModel() {
val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0)
val fakePerson = fakePersonModel("id", "name", fakeClickIntent)
@@ -99,16 +95,17 @@
val mockClickView = mock(View::class.java)
factoryDataSource.registerListener(fakeListener)
+
val viewModel = (fakeListener.lastSeen as Maybe.Just).value
.createWithAssociatedClickView(mockClickView)
assertThat(viewModel.isVisible).isTrue()
-
val people = viewModel.people.toList()
assertThat(people.size).isEqualTo(1)
assertThat(people[0].name).isEqualTo("name")
assertThat(people[0].icon).isSameAs(fakePerson.avatar)
people[0].onClick()
+
verify(mockActivityStarter).startPendingIntentDismissingKeyguard(
same(fakeClickIntent),
any(),
@@ -117,16 +114,20 @@
}
}
+/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
private inline fun <reified T : Any> any(): T {
return Mockito.any() ?: createInstance(T::class)
}
+/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
private inline fun <reified T : Any> same(value: T): T {
return Mockito.same(value) ?: createInstance(T::class)
}
+/** Creates an instance of the given class. */
private fun <T : Any> createInstance(clazz: KClass<T>): T = castNull()
+/** Tricks the Kotlin compiler into assigning `null` to a non-nullable variable. */
@Suppress("UNCHECKED_CAST")
private fun <T> castNull(): T = null as T
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index ca69c18..998572f 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -21,6 +21,7 @@
"src/**/*.java",
":framework-tethering-shared-srcs",
":services-tethering-shared-srcs",
+ ":servicescore-tethering-src",
],
static_libs: [
"androidx.annotation_annotation",
@@ -67,13 +68,23 @@
// This group will be removed when tethering migration is done.
filegroup {
- name: "tethering-services-srcs",
+ name: "tethering-servicescore-srcs",
srcs: [
+ "src/com/android/server/connectivity/tethering/EntitlementManager.java",
"src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
+ "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java",
+ ],
+}
+
+// This group will be removed when tethering migration is done.
+filegroup {
+ name: "tethering-servicesnet-srcs",
+ srcs: [
"src/android/net/dhcp/DhcpServerCallbacks.java",
"src/android/net/dhcp/DhcpServingParamsParcelExt.java",
"src/android/net/ip/IpServer.java",
"src/android/net/ip/RouterAdvertisementDaemon.java",
"src/android/net/util/InterfaceSet.java",
+ "src/android/net/util/PrefixUtils.java",
],
}
diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml
index b9cac19..dc013da 100644
--- a/packages/Tethering/AndroidManifestBase.xml
+++ b/packages/Tethering/AndroidManifestBase.xml
@@ -23,7 +23,6 @@
<application
android:label="Tethering"
android:defaultToDeviceProtectedStorage="true"
- android:directBootAware="true"
- android:usesCleartextTraffic="true">
+ android:directBootAware="true">
</application>
</manifest>
diff --git a/services/net/java/android/net/util/PrefixUtils.java b/packages/Tethering/src/android/net/util/PrefixUtils.java
similarity index 92%
rename from services/net/java/android/net/util/PrefixUtils.java
rename to packages/Tethering/src/android/net/util/PrefixUtils.java
index f60694a..f203e99 100644
--- a/services/net/java/android/net/util/PrefixUtils.java
+++ b/packages/Tethering/src/android/net/util/PrefixUtils.java
@@ -42,16 +42,19 @@
public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24");
+ /** Get non forwardable prefixes. */
public static Set<IpPrefix> getNonForwardablePrefixes() {
final HashSet<IpPrefix> prefixes = new HashSet<>();
addNonForwardablePrefixes(prefixes);
return prefixes;
}
+ /** Add non forwardable prefixes. */
public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) {
Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES);
}
+ /** Get local prefixes from |lp|. */
public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) {
final HashSet<IpPrefix> localPrefixes = new HashSet<>();
if (lp == null) return localPrefixes;
@@ -66,10 +69,12 @@
return localPrefixes;
}
+ /** Convert LinkAddress |addr| to IpPrefix. */
public static IpPrefix asIpPrefix(LinkAddress addr) {
return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
}
+ /** Convert InetAddress |ip| to IpPrefix. */
public static IpPrefix ipAddressAsPrefix(InetAddress ip) {
final int bitLength = (ip instanceof Inet4Address)
? NetworkConstants.IPV4_ADDR_BITS
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
similarity index 99%
rename from services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index f952bce..6b0f1de 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -87,7 +87,6 @@
private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
-
// The ArraySet contains enabled downstream types, ex:
// {@link ConnectivityManager.TETHERING_WIFI}
// {@link ConnectivityManager.TETHERING_USB}
@@ -112,7 +111,6 @@
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
int permissionChangeMessageCode, MockableSystemProperties systemProperties) {
-
mContext = ctx;
mLog = log.forSubComponent(TAG);
mCurrentTethers = new ArraySet<Integer>();
@@ -138,7 +136,7 @@
/**
* Ui entitlement check fails in |downstream|.
*
- * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}.
+ * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}.
*/
void onUiEntitlementFailed(int downstream);
}
@@ -662,7 +660,6 @@
private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
-
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
if (!isTetherProvisioningRequired(config)) {
receiver.send(TETHER_ERROR_NO_ERROR, null);
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
similarity index 93%
rename from services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3a9e21f..9769596 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -146,6 +146,7 @@
}
}
+ /** Listen all networks. */
public void startObserveAllNetworks() {
stop();
@@ -155,6 +156,13 @@
cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
}
+ /**
+ * Stop tracking candidate tethering upstreams and release mobile network request.
+ * Note: this function is used when tethering is stopped because tethering do not need to
+ * choose upstream anymore. But it would not stop default network tracking because
+ * EntitlementManager may need to know default network to decide whether to request entitlement
+ * check even tethering is not active yet.
+ */
public void stop() {
releaseMobileNetworkRequest();
@@ -165,6 +173,7 @@
mNetworkMap.clear();
}
+ /** Setup or teardown DUN connection according to |dunRequired|. */
public void updateMobileRequiresDun(boolean dunRequired) {
final boolean valueChanged = (mDunRequired != dunRequired);
mDunRequired = dunRequired;
@@ -174,10 +183,12 @@
}
}
+ /** Whether mobile network is requested. */
public boolean mobileNetworkRequested() {
return (mMobileNetworkCallback != null);
}
+ /** Request mobile network if mobile upstream is permitted. */
public void registerMobileNetworkRequest() {
if (!isCellularUpstreamPermitted()) {
mLog.i("registerMobileNetworkRequest() is not permitted");
@@ -209,6 +220,7 @@
cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
}
+ /** Release mobile network request. */
public void releaseMobileNetworkRequest() {
if (mMobileNetworkCallback == null) return;
@@ -221,6 +233,9 @@
// becomes available and useful we (a) file a request to keep it up as
// necessary and (b) change all upstream tracking state accordingly (by
// passing LinkProperties up to Tethering).
+ /**
+ * Select the first available network from |perferredTypes|.
+ */
public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
@@ -254,7 +269,11 @@
return typeStatePair.ns;
}
- // Returns null if no current upstream available.
+ /**
+ * Get current preferred upstream network. If default network is cellular and DUN is required,
+ * preferred upstream would be DUN otherwise preferred upstream is the same as default network.
+ * Returns null if no current upstream is available.
+ */
public NetworkState getCurrentPreferredUpstream() {
final NetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
@@ -270,10 +289,12 @@
return findFirstDunNetwork(mNetworkMap.values());
}
+ /** Tell UpstreamNetworkMonitor which network is the current upstream of tethering. */
public void setCurrentUpstream(Network upstream) {
mTetheringUpstreamNetwork = upstream;
}
+ /** Return local prefixes. */
public Set<IpPrefix> getLocalPrefixes() {
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
@@ -501,8 +522,8 @@
try {
nc = ConnectivityManager.networkCapabilitiesForType(type);
} catch (IllegalArgumentException iae) {
- Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " +
- ConnectivityManager.getNetworkTypeName(type));
+ Log.e(TAG, "No NetworkCapabilities mapping for legacy type: "
+ + ConnectivityManager.getNetworkTypeName(type));
continue;
}
if (!isCellularUpstreamPermitted && isCellular(nc)) {
@@ -547,18 +568,18 @@
}
private static boolean isCellular(NetworkCapabilities nc) {
- return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) &&
- nc.hasCapability(NET_CAPABILITY_NOT_VPN);
+ return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR)
+ && nc.hasCapability(NET_CAPABILITY_NOT_VPN);
}
private static boolean hasCapability(NetworkState ns, int netCap) {
- return (ns != null) && (ns.networkCapabilities != null) &&
- ns.networkCapabilities.hasCapability(netCap);
+ return (ns != null) && (ns.networkCapabilities != null)
+ && ns.networkCapabilities.hasCapability(netCap);
}
private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
- return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) &&
- !isCellular(ns.networkCapabilities);
+ return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null)
+ && !isCellular(ns.networkCapabilities);
}
private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index da62107..7c06e5f 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -17,7 +17,10 @@
android_test {
name: "TetheringTests",
certificate: "platform",
- srcs: ["src/**/*.java"],
+ srcs: [
+ ":servicescore-tethering-src",
+ "src/**/*.java",
+ ],
test_suites: ["device-tests"],
static_libs: [
"androidx.test.rules",
@@ -42,7 +45,9 @@
filegroup {
name: "tethering-tests-src",
srcs: [
+ "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java",
"src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
+ "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java",
"src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
"src/android/net/ip/IpServerTest.java",
"src/android/net/util/InterfaceSetTest.java",
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
rename to packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
similarity index 94%
rename from tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
rename to packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 0d276cb..c028d6d 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -87,7 +87,7 @@
// Actual contents of the request don't matter for this test. The lack of
// any specific TRANSPORT_* is sufficient to identify this request.
- private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+ private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build();
@Mock private Context mContext;
@Mock private EntitlementManager mEntitleMgr;
@@ -140,7 +140,7 @@
@Test
public void testDefaultNetworkIsTracked() throws Exception {
assertTrue(mCM.hasNoCallbacks());
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
assertEquals(1, mCM.trackingDefault.size());
@@ -153,7 +153,7 @@
public void testListensForAllNetworks() throws Exception {
assertTrue(mCM.listening.isEmpty());
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
assertFalse(mCM.listening.isEmpty());
assertTrue(mCM.isListeningForAll());
@@ -164,9 +164,9 @@
@Test
public void testCallbacksRegistered() {
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
verify(mCM, times(1)).requestNetwork(
- eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
+ eq(sDefaultRequest), any(NetworkCallback.class), any(Handler.class));
mUNM.startObserveAllNetworks();
verify(mCM, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
@@ -191,7 +191,7 @@
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
- assertFalse(mCM.isDunRequested());
+ assertFalse(isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
@@ -217,7 +217,7 @@
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(mCM.isDunRequested());
+ assertTrue(isDunRequested());
// Try a few things that must not result in any state change.
mUNM.registerMobileNetworkRequest();
@@ -226,7 +226,7 @@
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(mCM.isDunRequested());
+ assertTrue(isDunRequested());
mUNM.stop();
verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
@@ -250,7 +250,7 @@
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(mCM.isDunRequested());
+ assertTrue(isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
@@ -266,17 +266,17 @@
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
- assertFalse(mCM.isDunRequested());
+ assertFalse(isDunRequested());
mUNM.updateMobileRequiresDun(true);
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(mCM.isDunRequested());
+ assertTrue(isDunRequested());
// Test going from DUN to no-DUN correctly re-registers callbacks.
mUNM.updateMobileRequiresDun(false);
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
- assertFalse(mCM.isDunRequested());
+ assertFalse(isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
@@ -287,7 +287,7 @@
final Collection<Integer> preferredTypes = new ArrayList<>();
preferredTypes.add(TYPE_WIFI);
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -369,7 +369,7 @@
@Test
public void testGetCurrentPreferredUpstream() throws Exception {
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
mUNM.updateMobileRequiresDun(false);
@@ -418,7 +418,7 @@
@Test
public void testLocalPrefixes() throws Exception {
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
// [0] Test minimum set of local prefixes.
@@ -431,13 +431,13 @@
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
final LinkProperties wifiLp = wifiAgent.linkProperties;
wifiLp.setInterfaceName("wlan0");
- final String[] WIFI_ADDRS = {
+ final String[] wifi_addrs = {
"fe80::827a:bfff:fe6f:374d", "100.112.103.18",
"2001:db8:4:fd00:827a:bfff:fe6f:374d",
"2001:db8:4:fd00:6dea:325a:fdae:4ef4",
"fd6a:a640:60bf:e985::123", // ULA address for good measure.
};
- for (String addrStr : WIFI_ADDRS) {
+ for (String addrStr : wifi_addrs) {
final String cidr = addrStr.contains(":") ? "/64" : "/20";
wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
@@ -458,10 +458,10 @@
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
final LinkProperties cellLp = cellAgent.linkProperties;
cellLp.setInterfaceName("rmnet_data0");
- final String[] CELL_ADDRS = {
+ final String[] cell_addrs = {
"10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
};
- for (String addrStr : CELL_ADDRS) {
+ for (String addrStr : cell_addrs) {
final String cidr = addrStr.contains(":") ? "/64" : "/27";
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
@@ -481,10 +481,10 @@
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
final LinkProperties dunLp = dunAgent.linkProperties;
dunLp.setInterfaceName("rmnet_data1");
- final String[] DUN_ADDRS = {
+ final String[] dun_addrs = {
"192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
};
- for (String addrStr : DUN_ADDRS) {
+ for (String addrStr : dun_addrs) {
final String cidr = addrStr.contains(":") ? "/64" : "/27";
dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
@@ -525,7 +525,7 @@
// Mobile has higher pirority than wifi.
preferredTypes.add(TYPE_MOBILE_HIPRI);
preferredTypes.add(TYPE_WIFI);
- mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
// Setup wifi and make wifi as default network.
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
@@ -556,6 +556,15 @@
mCM.legacyTypeMap.values().iterator().next());
}
+ private boolean isDunRequested() {
+ for (NetworkRequest req : mCM.requested.values()) {
+ if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static class TestConnectivityManager extends ConnectivityManager {
public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
public Set<NetworkCallback> trackingDefault = new HashSet<>();
@@ -598,17 +607,10 @@
return false;
}
- boolean isDunRequested() {
- for (NetworkRequest req : requested.values()) {
- if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
- return true;
- }
- }
- return false;
+ int getNetworkId() {
+ return ++mNetworkId;
}
- int getNetworkId() { return ++mNetworkId; }
-
void makeDefaultNetwork(TestNetworkAgent agent) {
if (Objects.equals(defaultNetwork, agent)) return;
@@ -630,7 +632,7 @@
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
assertFalse(allCallbacks.containsKey(cb));
allCallbacks.put(cb, h);
- if (mDefaultRequest.equals(req)) {
+ if (sDefaultRequest.equals(req)) {
assertFalse(trackingDefault.contains(cb));
trackingDefault.add(cb);
} else {
@@ -749,9 +751,13 @@
private final State mLoggingState = new LoggingState();
class LoggingState extends State {
- @Override public void enter() { messages.clear(); }
+ @Override public void enter() {
+ messages.clear();
+ }
- @Override public void exit() { messages.clear(); }
+ @Override public void exit() {
+ messages.clear();
+ }
@Override public boolean processMessage(Message msg) {
messages.add(msg);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 68e11df32..6f43529 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -67,8 +67,6 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.provider.SettingsStringUtil;
-import android.provider.SettingsStringUtil.ComponentNameSet;
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -2219,12 +2217,12 @@
* Enables accessibility service specified by {@param componentName} for the {@param userId}.
*/
private void enableAccessibilityServiceLocked(ComponentName componentName, int userId) {
- final SettingStringHelper setting =
- new SettingStringHelper(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userId);
- setting.write(ComponentNameSet.add(setting.read(), componentName));
+ mTempComponentNameSet.clear();
+ readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId, mTempComponentNameSet);
+ mTempComponentNameSet.add(componentName);
+ persistComponentNamesToSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ mTempComponentNameSet, userId);
AccessibilityUserState userState = getUserStateLocked(userId);
if (userState.mEnabledServices.add(componentName)) {
@@ -2236,12 +2234,12 @@
* Disables accessibility service specified by {@param componentName} for the {@param userId}.
*/
private void disableAccessibilityServiceLocked(ComponentName componentName, int userId) {
- final SettingsStringUtil.SettingStringHelper setting =
- new SettingStringHelper(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userId);
- setting.write(ComponentNameSet.remove(setting.read(), componentName));
+ mTempComponentNameSet.clear();
+ readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId, mTempComponentNameSet);
+ mTempComponentNameSet.remove(componentName);
+ persistComponentNamesToSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ mTempComponentNameSet, userId);
AccessibilityUserState userState = getUserStateLocked(userId);
if (userState.mEnabledServices.remove(componentName)) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 3067beb..c865384 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -80,6 +80,7 @@
":vold_aidl",
":gsiservice_aidl",
":platform-compat-config",
+ ":tethering-servicescore-srcs",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
@@ -155,3 +156,11 @@
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
}
+
+// TODO: this should be removed after tethering migration done.
+filegroup {
+ name: "servicescore-tethering-src",
+ srcs: [
+ "java/com/android/server/connectivity/MockableSystemProperties.java",
+ ],
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ff0044f..1ce1137 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -154,7 +154,7 @@
static final int TICK_HISTORY_DEPTH = 10;
static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
- // Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
+ // Indices into the KEYS_APP_STANDBY_QUOTAS array.
static final int ACTIVE_INDEX = 0;
static final int WORKING_INDEX = 1;
static final int FREQUENT_INDEX = 2;
@@ -401,8 +401,6 @@
static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
@VisibleForTesting
static final String KEY_MAX_ALARMS_PER_UID = "max_alarms_per_uid";
- @VisibleForTesting
- static final String KEY_APP_STANDBY_QUOTAS_ENABLED = "app_standby_quotas_enabled";
private static final String KEY_APP_STANDBY_WINDOW = "app_standby_window";
@VisibleForTesting
final String[] KEYS_APP_STANDBY_QUOTAS = {
@@ -413,15 +411,6 @@
"standby_never_quota",
};
- // Keys for specifying throttling delay based on app standby bucketing
- private final String[] KEYS_APP_STANDBY_DELAY = {
- "standby_active_delay",
- "standby_working_delay",
- "standby_frequent_delay",
- "standby_rare_delay",
- "standby_never_delay",
- };
-
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
@@ -430,7 +419,6 @@
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
- private static final boolean DEFAULT_APP_STANDBY_QUOTAS_ENABLED = true;
private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000; // 1 hr
/**
* Max number of times an app can receive alarms in {@link #APP_STANDBY_WINDOW}
@@ -442,13 +430,6 @@
1, // Rare
0 // Never
};
- private final long[] DEFAULT_APP_STANDBY_DELAYS = {
- 0, // Active
- 6 * 60_000, // Working
- 30 * 60_000, // Frequent
- 2 * 60 * 60_000, // Rare
- 10 * 24 * 60 * 60_000 // Never
- };
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -473,10 +454,7 @@
public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
public int MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID;
- public boolean APP_STANDBY_QUOTAS_ENABLED = DEFAULT_APP_STANDBY_QUOTAS_ENABLED;
-
public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
- public long[] APP_STANDBY_MIN_DELAYS = new long[DEFAULT_APP_STANDBY_DELAYS.length];
public int[] APP_STANDBY_QUOTAS = new int[DEFAULT_APP_STANDBY_QUOTAS.length];
private ContentResolver mResolver;
@@ -532,16 +510,6 @@
DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
DEFAULT_LISTENER_TIMEOUT);
- APP_STANDBY_MIN_DELAYS[ACTIVE_INDEX] = mParser.getDurationMillis(
- KEYS_APP_STANDBY_DELAY[ACTIVE_INDEX],
- DEFAULT_APP_STANDBY_DELAYS[ACTIVE_INDEX]);
- for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_DELAY.length; i++) {
- APP_STANDBY_MIN_DELAYS[i] = mParser.getDurationMillis(KEYS_APP_STANDBY_DELAY[i],
- Math.max(APP_STANDBY_MIN_DELAYS[i - 1], DEFAULT_APP_STANDBY_DELAYS[i]));
- }
-
- APP_STANDBY_QUOTAS_ENABLED = mParser.getBoolean(KEY_APP_STANDBY_QUOTAS_ENABLED,
- DEFAULT_APP_STANDBY_QUOTAS_ENABLED);
APP_STANDBY_WINDOW = mParser.getLong(KEY_APP_STANDBY_WINDOW,
DEFAULT_APP_STANDBY_WINDOW);
@@ -614,15 +582,6 @@
pw.print(KEY_MAX_ALARMS_PER_UID); pw.print("=");
pw.println(MAX_ALARMS_PER_UID);
- for (int i = 0; i < KEYS_APP_STANDBY_DELAY.length; i++) {
- pw.print(KEYS_APP_STANDBY_DELAY[i]); pw.print("=");
- TimeUtils.formatDuration(APP_STANDBY_MIN_DELAYS[i], pw);
- pw.println();
- }
-
- pw.print(KEY_APP_STANDBY_QUOTAS_ENABLED); pw.print("=");
- pw.println(APP_STANDBY_QUOTAS_ENABLED);
-
pw.print(KEY_APP_STANDBY_WINDOW); pw.print("=");
TimeUtils.formatDuration(APP_STANDBY_WINDOW, pw);
pw.println();
@@ -1826,27 +1785,6 @@
}
/**
- * Return the minimum time that should elapse before an app in the specified bucket
- * can receive alarms again
- */
- @VisibleForTesting
- long getMinDelayForBucketLocked(int bucket) {
- // UsageStats bucket values are treated as floors of their behavioral range.
- // In other words, a bucket value between WORKING and ACTIVE is treated as
- // WORKING, not as ACTIVE. The ACTIVE and NEVER bucket apply only at specific
- // values.
- final int index;
-
- if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) index = NEVER_INDEX;
- else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) index = RARE_INDEX;
- else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) index = FREQUENT_INDEX;
- else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) index = WORKING_INDEX;
- else index = ACTIVE_INDEX;
-
- return mConstants.APP_STANDBY_MIN_DELAYS[index];
- }
-
- /**
* Adjusts the alarm delivery time based on the current app standby bucket.
* @param alarm The alarm to adjust
* @return true if the alarm delivery time was updated.
@@ -1872,50 +1810,34 @@
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
- if (mConstants.APP_STANDBY_QUOTAS_ENABLED) {
- // Quota deferring implementation:
- final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
- sourceUserId);
- final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
- boolean deferred = false;
- if (wakeupsInWindow >= quotaForBucket) {
- final long minElapsed;
- if (quotaForBucket <= 0) {
- // Just keep deferring for a day till the quota changes
- minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
- } else {
- // Suppose the quota for window was q, and the qth last delivery time for this
- // package was t(q) then the next delivery must be after t(q) + <window_size>
- final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
- sourceUserId, quotaForBucket);
- minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
- }
- if (alarm.expectedWhenElapsed < minElapsed) {
- alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
- deferred = true;
- }
+ // Quota deferring implementation:
+ final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
+ sourceUserId);
+ final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
+ boolean deferred = false;
+ if (wakeupsInWindow >= quotaForBucket) {
+ final long minElapsed;
+ if (quotaForBucket <= 0) {
+ // Just keep deferring for a day till the quota changes
+ minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
+ } else {
+ // Suppose the quota for window was q, and the qth last delivery time for this
+ // package was t(q) then the next delivery must be after t(q) + <window_size>
+ final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
+ sourceUserId, quotaForBucket);
+ minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
}
- if (!deferred) {
- // Restore original requirements in case they were changed earlier.
- alarm.whenElapsed = alarm.expectedWhenElapsed;
- alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
- }
- } else {
- // Minimum delay deferring implementation:
- final long lastElapsed = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
- sourceUserId, 1);
- if (lastElapsed > 0) {
- final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
- if (alarm.expectedWhenElapsed < minElapsed) {
- alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
- } else {
- // app is now eligible to run alarms at the originally requested window.
- // Restore original requirements in case they were changed earlier.
- alarm.whenElapsed = alarm.expectedWhenElapsed;
- alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
- }
+ if (alarm.expectedWhenElapsed < minElapsed) {
+ alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+ deferred = true;
}
}
+ if (!deferred) {
+ // Restore original requirements in case they were changed earlier.
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
+ }
+
return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
}
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index aeb3e7f..028d412 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
@@ -40,11 +41,13 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.dropbox.DropBoxManagerServiceDumpProto;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -65,6 +68,7 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.GZIPOutputStream;
@@ -85,6 +89,9 @@
private static final boolean PROFILE_DUMP = false;
+ // Max number of bytes of a dropbox entry to write into protobuf.
+ private static final int PROTO_MAX_DATA_BYTES = 256 * 1024;
+
// TODO: This implementation currently uses one file per entry, which is
// inefficient for smallish entries -- consider using a single queue file
// per tag (or even globally) instead.
@@ -464,6 +471,14 @@
}
private boolean checkPermission(int callingUid, String callingPackage) {
+ // If callers have this permission, then we don't need to check
+ // USAGE_STATS, because they are part of the system and have agreed to
+ // check USAGE_STATS before passing the data along.
+ if (getContext().checkCallingPermission(android.Manifest.permission.PEEK_DROPBOX_DATA)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
// Callers always need this permission
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.READ_LOGS, TAG);
@@ -547,18 +562,22 @@
StringBuilder out = new StringBuilder();
boolean doPrint = false, doFile = false;
+ boolean dumpProto = false;
ArrayList<String> searchArgs = new ArrayList<String>();
for (int i = 0; args != null && i < args.length; i++) {
if (args[i].equals("-p") || args[i].equals("--print")) {
doPrint = true;
} else if (args[i].equals("-f") || args[i].equals("--file")) {
doFile = true;
+ } else if (args[i].equals("--proto")) {
+ dumpProto = true;
} else if (args[i].equals("-h") || args[i].equals("--help")) {
pw.println("Dropbox (dropbox) dump options:");
pw.println(" [-h|--help] [-p|--print] [-f|--file] [timestamp]");
pw.println(" -h|--help: print this help");
pw.println(" -p|--print: print full contents of each entry");
pw.println(" -f|--file: print path of each entry's file");
+ pw.println(" --proto: dump data to proto");
pw.println(" [timestamp] optionally filters to only those entries.");
return;
} else if (args[i].startsWith("-")) {
@@ -568,6 +587,11 @@
}
}
+ if (dumpProto) {
+ dumpProtoLocked(fd, searchArgs);
+ return;
+ }
+
out.append("Drop box contents: ").append(mAllFiles.contents.size()).append(" entries\n");
out.append("Max entries: ").append(mMaxFiles).append("\n");
@@ -581,19 +605,15 @@
out.append("\n");
}
- int numFound = 0, numArgs = searchArgs.size();
+ int numFound = 0;
out.append("\n");
for (EntryFile entry : mAllFiles.contents) {
- String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis);
- boolean match = true;
- for (int i = 0; i < numArgs && match; i++) {
- String arg = searchArgs.get(i);
- match = (date.contains(arg) || arg.equals(entry.tag));
- }
- if (!match) continue;
+ if (!matchEntry(entry, searchArgs)) continue;
numFound++;
if (doPrint) out.append("========================================\n");
+
+ String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis);
out.append(date).append(" ").append(entry.tag == null ? "(no tag)" : entry.tag);
final File file = entry.getFile(mDropBoxDir);
@@ -679,6 +699,55 @@
if (PROFILE_DUMP) Debug.stopMethodTracing();
}
+ private boolean matchEntry(EntryFile entry, ArrayList<String> searchArgs) {
+ String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis);
+ boolean match = true;
+ int numArgs = searchArgs.size();
+ for (int i = 0; i < numArgs && match; i++) {
+ String arg = searchArgs.get(i);
+ match = (date.contains(arg) || arg.equals(entry.tag));
+ }
+ return match;
+ }
+
+ private void dumpProtoLocked(FileDescriptor fd, ArrayList<String> searchArgs) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ for (EntryFile entry : mAllFiles.contents) {
+ if (!matchEntry(entry, searchArgs)) continue;
+
+ final File file = entry.getFile(mDropBoxDir);
+ if ((file == null) || ((entry.flags & DropBoxManager.IS_EMPTY) != 0)) {
+ continue;
+ }
+
+ final long bToken = proto.start(DropBoxManagerServiceDumpProto.ENTRIES);
+ proto.write(DropBoxManagerServiceDumpProto.Entry.TIME_MS, entry.timestampMillis);
+ try (
+ DropBoxManager.Entry dbe = new DropBoxManager.Entry(
+ entry.tag, entry.timestampMillis, file, entry.flags);
+ InputStream is = dbe.getInputStream();
+ ) {
+ if (is != null) {
+ byte[] buf = new byte[PROTO_MAX_DATA_BYTES];
+ int readBytes = 0;
+ int n = 0;
+ while (n >= 0 && (readBytes += n) < PROTO_MAX_DATA_BYTES) {
+ n = is.read(buf, readBytes, PROTO_MAX_DATA_BYTES - readBytes);
+ }
+ proto.write(DropBoxManagerServiceDumpProto.Entry.DATA,
+ Arrays.copyOf(buf, readBytes));
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't read: " + file, e);
+ }
+
+ proto.end(bToken);
+ }
+
+ proto.flush();
+ }
+
///////////////////////////////////////////////////////////////////////////
/** Chronologically sorted list of {@link EntryFile} */
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0a63bf8..e9cca66 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -128,6 +128,32 @@
* updates and alerts.
*/
public class LocationManagerService extends ILocationManager.Stub {
+
+ /**
+ * Controls lifecycle of LocationManagerService.
+ */
+ public static class Lifecycle extends SystemService {
+
+ private LocationManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new LocationManagerService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.LOCATION_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mService.systemRunning();
+ }
+ }
+ }
+
private static final String TAG = "LocationManagerService";
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
@@ -234,8 +260,7 @@
@GuardedBy("mLock")
private final LocationUsageLogger mLocationUsageLogger;
- public LocationManagerService(Context context) {
- super();
+ private LocationManagerService(Context context) {
mContext = context;
mHandler = FgThread.getHandler();
mLocationUsageLogger = new LocationUsageLogger();
@@ -254,7 +279,7 @@
// most startup is deferred until systemRunning()
}
- public void systemRunning() {
+ private void systemRunning() {
synchronized (mLock) {
initializeLocked();
}
@@ -2051,6 +2076,18 @@
* Note: must be constructed with lock held.
*/
private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+ // translate expireIn value into expireAt
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long expireAt;
+ // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
+ if (request.getExpireIn() > Long.MAX_VALUE - elapsedRealtime) {
+ expireAt = Long.MAX_VALUE;
+ } else {
+ expireAt = Math.max(request.getExpireIn() + elapsedRealtime, 0);
+ }
+ request.setExpireAt(Math.min(request.getExpireAt(), expireAt));
+ request.setExpireIn(Long.MAX_VALUE);
+
mProvider = provider;
mRealRequest = request;
mRequest = request;
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index bfd4247..3bcb36f 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -18,10 +18,10 @@
import android.Manifest.permission;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -210,14 +210,9 @@
}
private boolean canAccessLocation(int uid, String packageName) {
- final PackageManager pm = mContext.getPackageManager();
- final AppOpsManager appOpsManager =
- (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- return isLocationModeEnabled()
- && pm.checkPermission(permission.ACCESS_COARSE_LOCATION, packageName)
- == PackageManager.PERMISSION_GRANTED
- && appOpsManager.noteOp(AppOpsManager.OP_COARSE_LOCATION, uid, packageName)
- == AppOpsManager.MODE_ALLOWED;
+ return isLocationModeEnabled() && PermissionChecker.checkPermissionForPreflight(mContext,
+ permission.ACCESS_COARSE_LOCATION, PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED;
}
private boolean isLocationModeEnabled() {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index e3dc3b7..7f51aa9 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -51,6 +51,8 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Find the best Service, and bind to it.
@@ -64,6 +66,7 @@
public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
+ private static final long BLOCKING_BINDER_TIMEOUT_MS = 30 * 1000;
/** Function to run on binder interface. */
public interface BinderRunner {
@@ -402,7 +405,7 @@
return defaultValue;
}
});
- } catch (InterruptedException e) {
+ } catch (InterruptedException | TimeoutException e) {
return defaultValue;
}
}
@@ -439,7 +442,8 @@
}
}
- private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException {
+ private <T> T runOnHandlerBlocking(Callable<T> c)
+ throws InterruptedException, TimeoutException {
if (Looper.myLooper() == mHandler.getLooper()) {
try {
return c.call();
@@ -451,7 +455,12 @@
FutureTask<T> task = new FutureTask<>(c);
mHandler.post(task);
try {
- return task.get();
+ // timeout will unblock callers, in particular if the caller is a binder thread to
+ // help reduce binder contention. this will still result in blocking the handler
+ // thread which may result in ANRs, but should make problems slightly more rare.
+ // the underlying solution is simply not to use this API at all, but that would
+ // require large refactors to very legacy code.
+ return task.get(BLOCKING_BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
// Function cannot throw exception, this should never happen
throw new IllegalStateException(e);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 4f54e64..b5cab1f 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3248,7 +3248,7 @@
UserAccounts accounts = getUserAccounts(userId);
logRecordWithUid(
accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
- userId);
+ uid);
new Session(accounts, response, accountType, expectActivityLaunch,
true /* stripAuthTokenFromResult */, null /* accountName */,
false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11cfe12..5df4543 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7758,7 +7758,7 @@
holder = getContentProviderExternalUnchecked(name, null, callingUid,
"*checkContentProviderUriPermission*", userId);
if (holder != null) {
- return holder.provider.checkUriPermission(null, uri, callingUid, modeFlags);
+ return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
}
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
@@ -7923,7 +7923,7 @@
sCallerIdentity.set(new Identity(
token, Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(null, uri, "r", null, token);
+ pfd = cph.provider.openFile(null, null, uri, "r", null, token);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6a29c75..8095020 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -383,7 +383,11 @@
// and then the delayed summary kill will be a no-op.
final ProcessRecord p = proc;
mService.mHandler.postDelayed(
- () -> killAppImmediateLocked(p, "forced", "killed for invalid state"),
+ () -> {
+ synchronized (mService) {
+ killAppImmediateLocked(p, "forced", "killed for invalid state");
+ }
+ },
5000L);
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 8575068..eecfdbd 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1182,8 +1182,8 @@
}
} else {
for (int j=0; j<ops.length; j++) {
- int code = uidState.opModes.keyAt(j);
- if (code >= 0) {
+ int code = ops[j];
+ if (uidState.opModes.indexOfKey(code) >= 0) {
if (resOps == null) {
resOps = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index bc5973d..8762435 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -20,6 +20,7 @@
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
+import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.compat.config.Change;
import java.util.HashMap;
@@ -35,12 +36,8 @@
*
* <p>Note, this class is not thread safe so callers must ensure thread safety.
*/
-public final class CompatChange {
+public final class CompatChange extends CompatibilityChangeInfo {
- private final long mChangeId;
- @Nullable private final String mName;
- private final int mEnableAfterTargetSdk;
- private final boolean mDisabled;
private Map<String, Boolean> mPackageOverrides;
public CompatChange(long changeId) {
@@ -56,29 +53,15 @@
*/
public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
boolean disabled) {
- mChangeId = changeId;
- mName = name;
- mEnableAfterTargetSdk = enableAfterTargetSdk;
- mDisabled = disabled;
+ super(changeId, name, enableAfterTargetSdk, disabled);
}
/**
* @param change an object generated by services/core/xsd/platform-compat-config.xsd
*/
public CompatChange(Change change) {
- mChangeId = change.getId();
- mName = change.getName();
- mEnableAfterTargetSdk = change.getEnableAfterTargetSdk();
- mDisabled = change.getDisabled();
- }
-
- long getId() {
- return mChangeId;
- }
-
- @Nullable
- String getName() {
- return mName;
+ super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
+ change.getDisabled());
}
/**
@@ -121,11 +104,11 @@
if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
return mPackageOverrides.get(app.packageName);
}
- if (mDisabled) {
+ if (getDisabled()) {
return false;
}
- if (mEnableAfterTargetSdk != -1) {
- return app.targetSdkVersion > mEnableAfterTargetSdk;
+ if (getEnableAfterTargetSdk() != -1) {
+ return app.targetSdkVersion > getEnableAfterTargetSdk();
}
return true;
}
@@ -133,14 +116,14 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
- .append(mChangeId);
- if (mName != null) {
- sb.append("; name=").append(mName);
+ .append(getId());
+ if (getName() != null) {
+ sb.append("; name=").append(getName());
}
- if (mEnableAfterTargetSdk != -1) {
- sb.append("; enableAfterTargetSdk=").append(mEnableAfterTargetSdk);
+ if (getEnableAfterTargetSdk() != -1) {
+ sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
}
- if (mDisabled) {
+ if (getDisabled()) {
sb.append("; disabled");
}
if (mPackageOverrides != null && mPackageOverrides.size() > 0) {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 0fabd9a..d6ec22b 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -16,6 +16,7 @@
package com.android.server.compat;
+import android.compat.Compatibility.ChangeConfig;
import android.content.pm.ApplicationInfo;
import android.os.Environment;
import android.text.TextUtils;
@@ -26,6 +27,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityChangeConfig;
+import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.XmlParser;
@@ -37,6 +39,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException;
/**
@@ -243,6 +247,49 @@
}
}
+ /**
+ * Get the config for a given app.
+ *
+ * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped.
+ * @return A {@link CompatibilityChangeConfig} which contains the compat config info for the
+ * given app.
+ */
+
+ public CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
+ Set<Long> enabled = new HashSet<>();
+ Set<Long> disabled = new HashSet<>();
+ synchronized (mChanges) {
+ for (int i = 0; i < mChanges.size(); ++i) {
+ CompatChange c = mChanges.valueAt(i);
+ if (c.isEnabled(applicationInfo)) {
+ enabled.add(c.getId());
+ } else {
+ disabled.add(c.getId());
+ }
+ }
+ }
+ return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled));
+ }
+
+ /**
+ * Dumps all the compatibility change information.
+ *
+ * @return An array of {@link CompatibilityChangeInfo} with the current changes.
+ */
+ public CompatibilityChangeInfo[] dumpChanges() {
+ synchronized (mChanges) {
+ CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
+ for (int i = 0; i < mChanges.size(); ++i) {
+ CompatChange change = mChanges.valueAt(i);
+ changeInfos[i] = new CompatibilityChangeInfo(change.getId(),
+ change.getName(),
+ change.getEnableAfterTargetSdk(),
+ change.getDisabled());
+ }
+ return changeInfos;
+ }
+ }
+
CompatConfig initConfigFromLib(File libraryDir) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.e(TAG, "No directory " + libraryDir + ", skipping");
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 9ac9955..75e2d22 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -25,6 +25,7 @@
import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.CompatibilityChangeConfig;
+import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -114,6 +115,16 @@
}
@Override
+ public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
+ return CompatConfig.get().getAppConfig(appInfo);
+ }
+
+ @Override
+ public CompatibilityChangeInfo[] listAllChanges() {
+ return CompatConfig.get().dumpChanges();
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
CompatConfig.get().dumpConfig(pw);
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index d9602b8..e6f0ed9 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -21,7 +21,7 @@
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
-import com.android.server.FgThread;
+import com.android.internal.os.BackgroundThread;
import com.android.server.ServiceWatcher;
import java.util.List;
@@ -53,7 +53,7 @@
int initialPackageNamesResId) {
mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
defaultServicePackageNameResId, initialPackageNamesResId,
- FgThread.getHandler());
+ BackgroundThread.getHandler());
}
private boolean bind() {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a2aeaf1..5c9b9c9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1593,6 +1593,7 @@
/** @return a specific user restriction that's in effect currently. */
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
return mLocalService.hasUserRestriction(restrictionKey, userId);
}
@@ -1717,6 +1718,7 @@
*/
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
}
@@ -4000,9 +4002,14 @@
long now = System.currentTimeMillis();
final long nowRealtime = SystemClock.elapsedRealtime();
- final int currentUser = LocalServices.getService(ActivityManagerInternal.class)
- .getCurrentUserId();
- pw.print("Current user: "); pw.println(currentUser);
+ final ActivityManagerInternal amInternal = LocalServices
+ .getService(ActivityManagerInternal.class);
+ pw.print("Current user: ");
+ if (amInternal != null) {
+ pw.println(amInternal.getCurrentUserId());
+ } else {
+ pw.println("N/A");
+ }
StringBuilder sb = new StringBuilder();
synchronized (mPackagesLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 9d41d97..f9b6bba 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -403,8 +403,7 @@
if (launchedActivity != null && launchedActivity.mDrawn) {
// Launched activity is already visible. We cannot measure windows drawn delay.
- reset(true /* abort */, info, "launched activity already visible",
- 0L /* timestampNs */);
+ abort(info, "launched activity already visible");
return;
}
@@ -422,8 +421,7 @@
if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
|| windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
// Failed to launch or it was not a process switch, so we don't care about the timing.
- reset(true /* abort */, info, "failed to launch or not a process switch",
- 0L /* timestampNs */);
+ abort(info, "failed to launch or not a process switch");
return;
} else if (otherWindowModesLaunching) {
// Don't log this windowing mode but continue with the other windowing modes.
@@ -469,8 +467,7 @@
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info);
if (allWindowsDrawn() && mLoggedTransitionStarting) {
- reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn",
- timestampNs /* timestampNs */);
+ reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
}
return infoSnapshot;
}
@@ -478,13 +475,13 @@
/**
* Notifies the tracker that the starting window was drawn.
*/
- void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestamp) {
+ void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestampNs) {
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
if (info == null || info.loggedStartingWindowDrawn) {
return;
}
info.loggedStartingWindowDrawn = true;
- info.startingWindowDelayMs = calculateDelay(timestamp);
+ info.startingWindowDelayMs = calculateDelay(timestampNs);
}
/**
@@ -544,10 +541,11 @@
mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget();
}
- private boolean hasVisibleNonFinishingActivity(TaskRecord t) {
+ /** @return {@code true} if the given task has an activity will be drawn. */
+ private static boolean hasActivityToBeDrawn(TaskRecord t) {
for (int i = t.getChildCount() - 1; i >= 0; --i) {
final ActivityRecord r = t.getChildAt(i);
- if (r.visible && !r.finishing) {
+ if (r.visible && !r.mDrawn && !r.finishing) {
return true;
}
}
@@ -574,19 +572,20 @@
return;
}
- // Check if there is any activity in the task that is visible and not finishing. If the
- // launched activity finished before it is drawn and if there is another activity in
- // the task then that activity will be draw on screen.
- if (hasVisibleNonFinishingActivity(t)) {
+ // If the task of the launched activity contains any activity to be drawn, then the
+ // window drawn event should report later to complete the transition. Otherwise all
+ // activities in this task may be finished, invisible or drawn, so the transition event
+ // should be cancelled.
+ if (hasActivityToBeDrawn(t)) {
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r);
logAppTransitionCancel(info);
- mWindowingModeTransitionInfo.remove(r.getWindowingMode());
- if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */, info, "notifyVisibilityChanged to invisible",
- 0L /* timestampNs */);
+ // Abort if this is the only one active transition.
+ if (mWindowingModeTransitionInfo.size() == 1
+ && mWindowingModeTransitionInfo.get(r.getWindowingMode()) != null) {
+ abort(info, "notifyVisibilityChanged to invisible");
}
}
}
@@ -622,19 +621,25 @@
&& mWindowingModeTransitionInfo.size() > 0;
}
+ /** Aborts tracking of current launch metrics. */
+ private void abort(WindowingModeTransitionInfo info, String cause) {
+ reset(true /* abort */, info, cause, 0L /* timestampNs */);
+ }
+
private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
- long timestampNs) {
+ long timestampNs) {
+ final boolean isAnyTransitionActive = isAnyTransitionActive();
if (DEBUG_METRICS) {
- Slog.i(TAG,
- "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs);
+ Slog.i(TAG, "reset abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
+ + " active=" + isAnyTransitionActive);
}
- if (!abort && isAnyTransitionActive()) {
+ if (!abort && isAnyTransitionActive) {
logAppTransitionMultiEvents();
}
stopLaunchTrace(info);
// Ignore reset-after reset.
- if (isAnyTransitionActive()) {
+ if (isAnyTransitionActive) {
// LaunchObserver callbacks.
if (abort) {
launchObserverNotifyActivityLaunchCancelled(info);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ec0900f..eaf19be 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -179,7 +179,7 @@
import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
-import static com.android.server.wm.AppWindowTokenProto.IS_REALLY_ANIMATING;
+import static com.android.server.wm.AppWindowTokenProto.IS_ANIMATING;
import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
import static com.android.server.wm.AppWindowTokenProto.LAST_SURFACE_SHOWING;
@@ -205,16 +205,17 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -291,16 +292,17 @@
import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
-import com.android.internal.R;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
@@ -315,6 +317,7 @@
import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService.H;
+import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
@@ -332,7 +335,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
-import java.util.function.Consumer;
/**
* An entry in the history stack, representing an activity.
@@ -551,24 +553,6 @@
private boolean mCurrentLaunchCanTurnScreenOn = true;
/**
- * This gets used during some open/close transitions as well as during a change transition
- * where it represents the starting-state snapshot.
- */
- private AppWindowThumbnail mThumbnail;
- private final Rect mTransitStartRect = new Rect();
-
- /**
- * If we are running an animation, this determines the transition type. Must be one of
- * AppTransition.TRANSIT_* constants.
- */
- private int mTransit;
-
- /**
- * If we are running an animation, this determines the flags during this animation. Must be a
- * bitwise combination of AppTransition.TRANSIT_FLAG_* constants.
- */
- private int mTransitFlags;
- /**
* This leash is used to "freeze" the app surface in place after the state change, but before
* the animation is ready to start.
*/
@@ -664,7 +648,6 @@
// TODO: Have a WindowContainer state for tracking exiting/deferred removal.
boolean mIsExiting;
- boolean mLaunchTaskBehind;
boolean mEnteringAnimation;
boolean mAppStopped;
@@ -676,15 +659,6 @@
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
- /** Whether this token should be boosted at the top of all app window tokens. */
- @VisibleForTesting boolean mNeedsZBoost;
-
- /** Layer used to constrain the animation to a token's stack bounds. */
- SurfaceControl mAnimationBoundsLayer;
-
- /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
- boolean mNeedsAnimationBoundsLayer;
-
private AppSaturationInfo mLastAppSaturationInfo;
private final ColorDisplayService.ColorTransformController mColorTransformController =
@@ -710,10 +684,6 @@
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
- private final Point mTmpPoint = new Point();
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpPrevBounds = new Rect();
-
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -2305,7 +2275,7 @@
* 2. App is delayed closing since it might enter PIP.
*/
boolean isClosingOrEnteringPip() {
- return (isAnimating() && hiddenRequested) || mWillCloseOrEnterPip;
+ return (isAnimating(TRANSITION | PARENTS) && hiddenRequested) || mWillCloseOrEnterPip;
}
/**
* @return Whether AppOps allows this package to enter picture-in-picture.
@@ -3114,7 +3084,7 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
- getAnimation(), isSelfAnimating());
+ getAnimation(), isAnimating(TRANSITION));
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "removeAppToken: %s"
+ " delayed=%b Callers=%s", this, delayed, Debug.getCallers(4));
@@ -3126,7 +3096,7 @@
// If this window was animating, then we need to ensure that the app transition notifies
// that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
// so add to that list now
- if (isSelfAnimating()) {
+ if (isAnimating(TRANSITION)) {
getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
}
@@ -3474,7 +3444,7 @@
* color mode set to avoid jank in the middle of the transition.
*/
boolean canShowWindows() {
- return allDrawn && !(isReallyAnimating() && hasNonDefaultColorWindow());
+ return allDrawn && !(isAnimating() && hasNonDefaultColorWindow());
}
/**
@@ -3536,17 +3506,17 @@
return forAllWindowsUnchecked(callback, traverseTopToBottom);
}
- @Override
- void forAllActivities(Consumer<ActivityRecord> callback) {
- callback.accept(this);
- }
-
boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
return super.forAllWindows(callback, traverseTopToBottom);
}
@Override
+ boolean forAllActivities(ToBooleanFunction<ActivityRecord> callback) {
+ return callback.apply(this);
+ }
+
+ @Override
protected void setLayer(Transaction t, int layer) {
if (!mSurfaceAnimator.hasLeash()) {
t.setLayer(mSurfaceControl, layer);
@@ -4127,8 +4097,8 @@
if (transit != WindowManager.TRANSIT_UNSET) {
if (mUseTransferredAnimation) {
- runningAppAnimation = isReallyAnimating();
- } else if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
+ runningAppAnimation = isAnimating();
+ } else if (applyAnimation(lp, transit, visible, isVoiceInteraction)) {
runningAppAnimation = true;
}
delayed = runningAppAnimation;
@@ -4179,21 +4149,14 @@
}
mUseTransferredAnimation = false;
- if (isReallyAnimating()) {
- delayed = true;
- } else {
+ delayed = isAnimating(CHILDREN);
+ if (!delayed) {
// We aren't animating anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be animating,
// which we might have done because we were in closing/opening apps list.
onAnimationFinished();
}
- for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
- if ((mChildren.get(i)).isSelfOrChildAnimating()) {
- delayed = true;
- }
- }
-
if (visibilityChanged) {
if (visible && !delayed) {
// The token was made immediately visible, there will be no entrance animation.
@@ -4208,7 +4171,7 @@
// updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
- if (visible || !isReallyAnimating()) {
+ if (visible || !isAnimating()) {
setClientHidden(!visible);
}
@@ -5288,13 +5251,13 @@
if (!allDrawn && w.mightAffectAllDrawn()) {
if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimationSet=" + isSelfAnimating());
+ + ", isAnimationSet=" + isAnimating(TRANSITION));
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ " pv=" + w.isVisibleByPolicy()
+ " mDrawState=" + winAnimator.drawStateToString()
+ " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
- + " a=" + isSelfAnimating());
+ + " a=" + isAnimating(TRANSITION));
}
}
@@ -5318,7 +5281,7 @@
}
}
} else if (w.isDrawnLw()) {
- onStartingWindowDrawn(SystemClock.uptimeMillis());
+ onStartingWindowDrawn(SystemClock.elapsedRealtimeNanos());
startingDisplayed = true;
}
}
@@ -5327,10 +5290,10 @@
}
/** Called when the starting window for this container is drawn. */
- private void onStartingWindowDrawn(long timestamp) {
+ private void onStartingWindowDrawn(long timestampNs) {
synchronized (mAtmService.mGlobalLock) {
mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
- getWindowingMode(), timestamp);
+ getWindowingMode(), timestampNs);
}
}
@@ -5722,28 +5685,44 @@
}
}
-
@VisibleForTesting
boolean shouldAnimate(int transit) {
+ final Task task = getTask();
+ if (task != null && !task.shouldAnimate()) {
+ return false;
+ }
final boolean isSplitScreenPrimary =
getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN;
- // Don't animate while the task runs recents animation but only if we are in the mode
- // where we cancel with deferred screenshot, which means that the controller has
- // transformed the task.
- final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
- if (controller != null && controller.isAnimatingTask(getTask())
- && controller.shouldDeferCancelUntilNextTransition()) {
- return false;
- }
-
// We animate always if it's not split screen primary, and only some special cases in split
// screen primary because it causes issues with stack clipping when we run an un-minimize
// animation at the same time.
return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
}
+ @Override
+ boolean isChangingAppTransition() {
+ final Task task = getTask();
+ if (task != null) {
+ return task.isChangingAppTransition();
+ }
+ return super.isChangingAppTransition();
+ }
+
+ @Override
+ boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+ boolean isVoiceInteraction) {
+ if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: transition animation is disabled or skipped. "
+ + "container=%s", this);
+ cancelAnimation();
+ return false;
+ }
+ return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
+ }
+
/**
* Creates a layer to apply crop to an animation.
*/
@@ -5757,171 +5736,6 @@
return boundsLayer;
}
- boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction) {
-
- if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: transition animation is disabled or skipped. "
- + "atoken=%s", this);
- cancelAnimation();
- return false;
- }
-
- // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
- // to animate and it can cause strange artifacts when we unfreeze the display if some
- // different animation is running.
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
- if (okToAnimate()) {
- final AnimationAdapter adapter;
- AnimationAdapter thumbnailAdapter = null;
-
- final int appStackClipMode =
- getDisplayContent().mAppTransition.getAppStackClipMode();
-
- // Separate position and size for use in animators.
- mTmpRect.set(getAnimationBounds(appStackClipMode));
- mTmpPoint.set(mTmpRect.left, mTmpRect.top);
- mTmpRect.offsetTo(0, 0);
-
- final boolean isChanging = AppTransition.isChangeTransit(transit) && enter
- && getDisplayContent().mChangingApps.contains(this);
-
- // Delaying animation start isn't compatible with remote animations at all.
- if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
- && !mSurfaceAnimator.isAnimationStartDelayed()) {
- RemoteAnimationController.RemoteAnimationRecord adapters =
- getDisplayContent().mAppTransition.getRemoteAnimationController()
- .createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
- (isChanging ? mTransitStartRect : null));
- adapter = adapters.mAdapter;
- thumbnailAdapter = adapters.mThumbnailAdapter;
- } else if (isChanging) {
- final float durationScale = mWmService.getTransitionAnimationScaleLocked();
- mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
- adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
- getDisplayContent().getDisplayInfo(), durationScale,
- true /* isAppAnimation */, false /* isThumbnail */),
- mWmService.mSurfaceAnimationRunner);
- if (mThumbnail != null) {
- thumbnailAdapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
- getDisplayContent().getDisplayInfo(), durationScale,
- true /* isAppAnimation */, true /* isThumbnail */),
- mWmService.mSurfaceAnimationRunner);
- }
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
-
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
- if (a != null) {
- // Only apply corner radius to animation if we're not in multi window mode.
- // We don't want rounded corners when in pip or split screen.
- final float windowCornerRadius = !inMultiWindowMode()
- ? getDisplayContent().getWindowCornerRadius()
- : 0;
- adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- getDisplayContent().mAppTransition.canSkipFirstFrame(),
- appStackClipMode,
- true /* isAppAnimation */,
- windowCornerRadius),
- mWmService.mSurfaceAnimationRunner);
- if (a.getZAdjustment() == Animation.ZORDER_TOP) {
- mNeedsZBoost = true;
- }
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- adapter = null;
- }
- }
- if (adapter != null) {
- startAnimation(getPendingTransaction(), adapter, !isVisible());
- if (adapter.getShowWallpaper()) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- }
- if (thumbnailAdapter != null) {
- mThumbnail.startAnimation(
- getPendingTransaction(), thumbnailAdapter, !isVisible());
- }
- }
- } else {
- cancelAnimation();
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-
- return isReallyAnimating();
- }
-
- private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction) {
- final DisplayContent displayContent = getTask().getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int width = displayInfo.appWidth;
- final int height = displayInfo.appHeight;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: atoken=%s", this);
-
- // Determine the visible rect to calculate the thumbnail clip
- final WindowState win = findMainWindow();
- final Rect frame = new Rect(0, 0, width, height);
- final Rect displayFrame = new Rect(0, 0,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
- final Rect insets = new Rect();
- final Rect stableInsets = new Rect();
- Rect surfaceInsets = null;
- final boolean freeform = win != null && win.inFreeformWindowingMode();
- if (win != null) {
- // Containing frame will usually cover the whole screen, including dialog windows.
- // For freeform workspace windows it will not cover the whole screen and it also
- // won't exactly match the final freeform window frame (e.g. when overlapping with
- // the status bar). In that case we need to use the final frame.
- if (freeform) {
- frame.set(win.getFrameLw());
- } else if (win.isLetterboxedAppWindow()) {
- frame.set(getTask().getBounds());
- } else if (win.isDockedResizing()) {
- // If we are animating while docked resizing, then use the stack bounds as the
- // animation target (which will be different than the task bounds)
- frame.set(getTask().getParent().getBounds());
- } else {
- frame.set(win.getContainingFrame());
- }
- surfaceInsets = win.getAttrs().surfaceInsets;
- // XXX(b/72757033): These are insets relative to the window frame, but we're really
- // interested in the insets relative to the frame we chose in the if-blocks above.
- win.getContentInsets(insets);
- win.getStableInsets(stableInsets);
- }
-
- if (mLaunchTaskBehind) {
- // Differentiate the two animations. This one which is briefly on the screen
- // gets the !enter animation, and the other activity which remains on the
- // screen gets the enter animation. Both appear in the mOpeningApps set.
- enter = false;
- }
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
- "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
- + "surfaceInsets=%s",
- AppTransition.appTransitionToString(transit), enter, frame, insets, surfaceInsets);
- final Configuration displayConfig = displayContent.getConfiguration();
- final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
- displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
- surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId);
- if (a != null) {
- if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
- final int containingWidth = frame.width();
- final int containingHeight = frame.height();
- a.initialize(containingWidth, containingHeight, width, height);
- a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
- }
- return a;
- }
-
@Override
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
return mAnimatingActivityRegistry != null
@@ -5941,6 +5755,7 @@
return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
}
+ @Override
boolean isWaitingForTransitionStart() {
final DisplayContent dc = getDisplayContent();
// TODO: Test for null can be removed once unification is done.
@@ -6069,10 +5884,7 @@
@Override
void prepareSurfaces() {
- // isSelfAnimating also returns true when we are about to start a transition, so we need
- // to check super here.
- final boolean reallyAnimating = super.isSelfAnimating();
- final boolean show = !isHidden() || reallyAnimating;
+ final boolean show = !isHidden() || isAnimating();
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
@@ -6100,14 +5912,13 @@
}
void attachThumbnailAnimation() {
- if (!isReallyAnimating()) {
+ if (!isAnimating()) {
return;
}
- final int taskId = getTask().mTaskId;
final GraphicBuffer thumbnailHeader =
- getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId);
+ getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(getTask());
if (thumbnailHeader == null) {
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "No thumbnail header bitmap for: %d", taskId);
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "No thumbnail header bitmap for: %s", getTask());
return;
}
clearThumbnail();
@@ -6121,7 +5932,7 @@
* {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
*/
void attachCrossProfileAppsThumbnailAnimation() {
- if (!isReallyAnimating()) {
+ if (!isAnimating()) {
return;
}
clearThumbnail();
@@ -6161,22 +5972,11 @@
final Rect insets = win != null ? win.getContentInsets() : null;
final Configuration displayConfig = mDisplayContent.getConfiguration();
return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
- appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
+ appRect, insets, thumbnailHeader, getTask(), displayConfig.uiMode,
displayConfig.orientation);
}
@Override
- boolean isAppAnimating() {
- return isSelfAnimating();
- }
-
- @Override
- boolean isSelfAnimating() {
- // If we are about to start a transition, we also need to be considered animating.
- return isWaitingForTransitionStart() || isReallyAnimating();
- }
-
- @Override
public void onAnimationLeashLost(Transaction t) {
super.onAnimationLeashLost(t);
if (mAnimationBoundsLayer != null) {
@@ -6281,15 +6081,6 @@
}
}
- /**
- * @return True if and only if we are actually running an animation. Note that
- * {@link #isSelfAnimating} also returns true if we are waiting for an animation to
- * start.
- */
- private boolean isReallyAnimating() {
- return super.isSelfAnimating();
- }
-
@Override
void cancelAnimation() {
cancelAnimationOnly();
@@ -6691,6 +6482,7 @@
}
@VisibleForTesting
+ @Override
Rect getAnimationBounds(int appStackClipMode) {
if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) {
// Using the stack bounds here effectively applies the clipping before animation.
@@ -7643,7 +7435,7 @@
super.writeToProto(proto, WINDOW_TOKEN, logLevel);
proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
- proto.write(IS_REALLY_ANIMATING, isReallyAnimating());
+ proto.write(IS_ANIMATING, isAnimating());
if (mThumbnail != null){
mThumbnail.writeToProto(proto, THUMBNAIL);
}
@@ -7765,4 +7557,35 @@
System.arraycopy(translation, 0, mTranslation, 0, mTranslation.length);
}
}
+
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final Task task = getTask();
+ final WindowState mainWindow = findMainWindow();
+ if (task == null || mainWindow == null) {
+ return null;
+ }
+ final Rect insets = new Rect();
+ mainWindow.getContentInsets(insets);
+ InsetUtils.addInsets(insets, getLetterboxInsets());
+ return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
+ record.mAdapter.mCapturedLeash, !task.fillsParent(),
+ mainWindow.mWinAnimator.mLastClipRect, insets,
+ getPrefixOrderIndex(), record.mAdapter.mPosition,
+ record.mAdapter.mStackBounds, task.getWindowConfiguration(),
+ false /*isNotInRecents*/,
+ record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
+ record.mStartBounds);
+ }
+
+ @Override
+ void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
+ Rect outSurfaceInsets) {
+ final WindowState win = findMainWindow();
+ if (win == null) {
+ return;
+ }
+ win.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 64351fb..87b41b4 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -82,6 +82,7 @@
import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import android.Manifest;
import android.app.Activity;
@@ -2127,7 +2128,7 @@
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
- final boolean animating = s.isSelfAnimating();
+ final boolean animating = s.isAnimating(TRANSITION);
if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+ " animating=" + animating + " finishing=" + s.finishing);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ff0dc54..f87175d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2354,7 +2354,7 @@
mMovedToFront = true;
}
- if (launchStack.topTask() == null) {
+ if (launchStack != null && launchStack.topTask() == null) {
// The task does not need to be reparented to the launch stack. Remove the
// launch stack if there is no activity in it.
Slog.w(TAG, "Removing an empty stack: " + launchStack);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1143c8..cb9a200 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -219,7 +219,7 @@
private int mNextAppTransitionExit;
private int mNextAppTransitionInPlace;
- // Keyed by task id.
+ // Keyed by WindowContainer hashCode.
private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
= new SparseArray<>();
private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture;
@@ -372,8 +372,9 @@
setAppTransitionState(APP_STATE_TIMEOUT);
}
- GraphicBuffer getAppTransitionThumbnailHeader(int taskId) {
- AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+ GraphicBuffer getAppTransitionThumbnailHeader(WindowContainer container) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+ container.hashCode());
if (spec == null) {
spec = mDefaultNextAppTransitionAnimationSpec;
}
@@ -789,14 +790,15 @@
}
}
- void getNextAppTransitionStartRect(int taskId, Rect rect) {
- AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+ void getNextAppTransitionStartRect(WindowContainer container, Rect rect) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+ container.hashCode());
if (spec == null) {
spec = mDefaultNextAppTransitionAnimationSpec;
}
if (spec == null || spec.rect == null) {
- Slog.e(TAG, "Starting rect for task: " + taskId + " requested, but not available",
- new Throwable());
+ Slog.e(TAG, "Starting rect for container: " + container
+ + " requested, but not available", new Throwable());
rect.setEmpty();
} else {
rect.set(spec.rect);
@@ -1065,7 +1067,7 @@
* when a thumbnail is specified with the pending animation override.
*/
Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
- GraphicBuffer thumbnailHeader, final int taskId, int uiMode, int orientation) {
+ GraphicBuffer thumbnailHeader, WindowContainer container, int uiMode, int orientation) {
Animation a;
final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -1073,7 +1075,7 @@
final int appWidth = appRect.width();
float scaleW = appWidth / thumbWidth;
- getNextAppTransitionStartRect(taskId, mTmpRect);
+ getNextAppTransitionStartRect(container, mTmpRect);
final float fromX;
float fromY;
final float toX;
@@ -1226,7 +1228,7 @@
Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
- int taskId) {
+ WindowContainer container) {
Animation a;
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
@@ -1244,10 +1246,10 @@
final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
if (freeform && scaleUp) {
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
- containingFrame, surfaceInsets, taskId);
+ containingFrame, surfaceInsets, container);
} else if (freeform) {
a = createAspectScaledThumbnailExitFreeformAnimationLocked(
- containingFrame, surfaceInsets, taskId);
+ containingFrame, surfaceInsets, container);
} else {
AnimationSet set = new AnimationSet(true);
@@ -1359,15 +1361,15 @@
}
private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
- @Nullable Rect surfaceInsets, int taskId) {
- getNextAppTransitionStartRect(taskId, mTmpRect);
+ @Nullable Rect surfaceInsets, WindowContainer container) {
+ getNextAppTransitionStartRect(container, mTmpRect);
return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
true);
}
private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
- @Nullable Rect surfaceInsets, int taskId) {
- getNextAppTransitionStartRect(taskId, mTmpRect);
+ @Nullable Rect surfaceInsets, WindowContainer container) {
+ getNextAppTransitionStartRect(container, mTmpRect);
return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
false);
}
@@ -1469,10 +1471,10 @@
* leaving, and the activity that is entering.
*/
Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
- int transit, int taskId) {
+ int transit, WindowContainer container) {
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
- final GraphicBuffer thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
+ final GraphicBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
Animation a;
getDefaultNextAppTransitionStartRect(mTmpRect);
final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
@@ -1615,7 +1617,7 @@
Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode,
int orientation, Rect frame, Rect displayFrame, Rect insets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
- boolean freeform, int taskId) {
+ boolean freeform, WindowContainer container) {
Animation a;
if (isKeyguardGoingAwayTransit(transit) && enter) {
a = loadKeyguardExitAnimation(transit);
@@ -1679,7 +1681,7 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
- frame, transit, taskId);
+ frame, transit, container);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
+ "Callers=%s",
@@ -1692,7 +1694,7 @@
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
a = createAspectScaledThumbnailEnterExitAnimationLocked(
getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
- insets, surfaceInsets, stableInsets, freeform, taskId);
+ insets, surfaceInsets, stableInsets, freeform, container);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
+ "Callers=%s",
@@ -1895,7 +1897,11 @@
for (int i = 0; i < specs.length; i++) {
AppTransitionAnimationSpec spec = specs[i];
if (spec != null) {
- mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
+ final WindowContainer container = findTask(spec.taskId);
+ if (container == null) {
+ continue;
+ }
+ mNextAppTransitionAnimationsSpecs.put(container.hashCode(), spec);
if (i == 0) {
// In full screen mode, the transition code depends on the default spec
// to be set.
@@ -1912,6 +1918,19 @@
}
}
+ private Task findTask(int taskId) {
+ if (taskId < 0) {
+ return null;
+ }
+ ArrayList<Task> tasks = new ArrayList<>();
+ mDisplayContent.forAllTasks(task -> {
+ if (task.mTaskId == taskId) {
+ tasks.add(task);
+ }
+ });
+ return tasks.size() == 1 ? tasks.get(0) : null;
+ }
+
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
boolean scaleUp) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3bda0c2..bef6af3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -410,7 +410,7 @@
ActivityRecord activity = apps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity);
activity.cancelAnimationOnly();
- activity.applyAnimationLocked(null, transit, true, false);
+ activity.applyAnimation(null, transit, true, false);
activity.updateReportedVisibilityLocked();
mService.openSurfaceTransaction();
try {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2d1d297..89568eb 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -106,6 +106,8 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
@@ -2363,7 +2365,7 @@
@Override
void removeIfPossible() {
- if (isAnimating()) {
+ if (isAnimating(TRANSITION | PARENTS)) {
mDeferredRemoval = true;
return;
}
@@ -3165,13 +3167,15 @@
// to look at all windows below the current target that are in this app, finding the
// highest visible one in layering.
WindowState highestTarget = null;
- if (activity.isSelfAnimating()) {
+ if (activity.isAnimating(TRANSITION)) {
highestTarget = activity.getHighestAnimLayerWindow(curTarget);
}
if (highestTarget != null) {
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget
- + " animating=" + highestTarget.isAnimating());
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, mAppTransition + " " + highestTarget + " animating="
+ + highestTarget.isAnimating(TRANSITION | PARENTS));
+ }
if (mAppTransition.isTransitionSet()) {
// If we are currently setting up for an animation, hold everything until we
@@ -4322,7 +4326,7 @@
// The split screen divider anchor is located above the split screen window.
layerForSplitScreenDividerAnchor = layer++;
}
- if (s.isTaskAnimating() || s.isAppAnimating()) {
+ if (s.isTaskAnimating() || s.isAppTransitioning()) {
// The animation layer is located above the highest animating stack and no
// higher.
layerForAnimationLayer = layer++;
@@ -4651,7 +4655,8 @@
// so it get's layered above the starting window.
if (imeTarget != null
&& !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow())
- && (!(imeTarget.inSplitScreenWindowingMode() || imeTarget.mToken.isAppAnimating())
+ && (!(imeTarget.inSplitScreenWindowingMode()
+ || imeTarget.mToken.isAppTransitioning())
&& (imeTarget.getSurfaceControl() != null))) {
mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
// TODO: We need to use an extra level on the app surface to ensure
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a783ee9..6e238b3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1920,10 +1920,18 @@
vf.set(displayFrames.mStable);
if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
- cf.bottom = displayFrames.mContent.bottom;
+ // cf.bottom should not be below the stable bottom, or the content might be obscured
+ // by the navigation bar.
+ if (cf.bottom > displayFrames.mContent.bottom) {
+ cf.bottom = displayFrames.mContent.bottom;
+ }
} else {
- cf.bottom = displayFrames.mDock.bottom;
- vf.bottom = displayFrames.mContent.bottom;
+ if (cf.bottom > displayFrames.mDock.bottom) {
+ cf.bottom = displayFrames.mDock.bottom;
+ }
+ if (vf.bottom > displayFrames.mContent.bottom) {
+ vf.bottom = displayFrames.mContent.bottom;
+ }
}
} else {
dcf.set(displayFrames.mSystem);
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 0c0cf92..e0a7b18 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+
import android.util.ArraySet;
import android.view.Display.Mode;
import android.view.DisplayInfo;
@@ -67,7 +70,7 @@
// If app is animating, it's not able to control refresh rate because we want the animation
// to run in default refresh rate.
- if (w.isAnimating()) {
+ if (w.isAnimating(TRANSITION | PARENTS)) {
return 0;
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c23ffd9..efd1241 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -41,7 +41,6 @@
import com.android.server.protolog.ProtoLogImpl;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
-import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -76,20 +75,20 @@
}
/**
- * Creates an animation record for each individual {@link ActivityRecord}.
+ * Creates an animation record for each individual {@link WindowContainer}.
*
- * @param activity The app to animate.
+ * @param windowContainer The windows to animate.
* @param position The position app bounds, in screen coordinates.
* @param stackBounds The stack bounds of the app relative to position.
* @param startBounds The stack bounds before the transition, in screen coordinates
* @return The record representing animation(s) to run on the app.
*/
- RemoteAnimationRecord createRemoteAnimationRecord(ActivityRecord activity, Point position,
- Rect stackBounds, Rect startBounds) {
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): token=%s",
- activity);
+ RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
+ Point position, Rect stackBounds, Rect startBounds) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
+ windowContainer);
final RemoteAnimationRecord adapters =
- new RemoteAnimationRecord(activity, position, stackBounds, startBounds);
+ new RemoteAnimationRecord(windowContainer, position, stackBounds, startBounds);
mPendingAnimations.add(adapters);
return adapters;
}
@@ -169,11 +168,12 @@
final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
if (target != null) {
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd token=%s", wrappers.mActivityRecord);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd container=%s",
+ wrappers.mWindowContainer);
targets.add(target);
} else {
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove token=%s",
- wrappers.mActivityRecord);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove container=%s",
+ wrappers.mWindowContainer);
// We can't really start an animation but we still need to make sure to finish the
// pending animation that was started by SurfaceAnimator
@@ -228,7 +228,8 @@
.onAnimationFinished(adapters.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tapp=%s", adapters.mActivityRecord);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",
+ adapters.mWindowContainer);
}
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
@@ -332,7 +333,7 @@
};
/**
- * Contains information about a remote-animation for one AppWindowToken. This keeps track of,
+ * Contains information about a remote-animation for one WindowContainer. This keeps track of,
* potentially, multiple animating surfaces (AdapterWrappers) associated with one
* Window/Transition. For example, a change transition has an adapter controller for the
* main window and an adapter controlling the start-state snapshot.
@@ -345,12 +346,12 @@
RemoteAnimationAdapterWrapper mAdapter;
RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
RemoteAnimationTarget mTarget;
- final ActivityRecord mActivityRecord;
+ final WindowContainer mWindowContainer;
final Rect mStartBounds;
- RemoteAnimationRecord(ActivityRecord activityRecord, Point endPos, Rect endBounds,
+ RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect endBounds,
Rect startBounds) {
- mActivityRecord = activityRecord;
+ mWindowContainer = windowContainer;
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds);
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
@@ -366,31 +367,20 @@
}
RemoteAnimationTarget createRemoteAnimationTarget() {
- final Task task = mActivityRecord.getTask();
- final WindowState mainWindow = mActivityRecord.findMainWindow();
- if (task == null || mainWindow == null || mAdapter == null
+ if (mAdapter == null
|| mAdapter.mCapturedFinishCallback == null
|| mAdapter.mCapturedLeash == null) {
return null;
}
- final Rect insets = new Rect();
- mainWindow.getContentInsets(insets);
- InsetUtils.addInsets(insets, mActivityRecord.getLetterboxInsets());
- mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
- mAdapter.mCapturedLeash, !mActivityRecord.fillsParent(),
- mainWindow.mWinAnimator.mLastClipRect, insets,
- mActivityRecord.getPrefixOrderIndex(), mAdapter.mPosition,
- mAdapter.mStackBounds, task.getWindowConfiguration(), false /*isNotInRecents*/,
- mThumbnailAdapter != null ? mThumbnailAdapter.mCapturedLeash : null,
- mStartBounds);
+ mTarget = mWindowContainer.createRemoteAnimationTarget(this);
return mTarget;
}
- private int getMode() {
- final DisplayContent dc = mActivityRecord.getDisplayContent();
- if (dc.mOpeningApps.contains(mActivityRecord)) {
+ int getMode() {
+ final DisplayContent dc = mWindowContainer.getDisplayContent();
+ if (dc.mOpeningApps.contains(mWindowContainer)) {
return RemoteAnimationTarget.MODE_OPENING;
- } else if (dc.mChangingApps.contains(mActivityRecord)) {
+ } else if (dc.mChangingApps.contains(mWindowContainer)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
@@ -398,12 +388,12 @@
}
}
- private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
+ class RemoteAnimationAdapterWrapper implements AnimationAdapter {
private final RemoteAnimationRecord mRecord;
SurfaceControl mCapturedLeash;
private OnAnimationFinishedCallback mCapturedFinishCallback;
- private final Point mPosition = new Point();
- private final Rect mStackBounds = new Rect();
+ final Point mPosition = new Point();
+ final Rect mStackBounds = new Rect();
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
Rect stackBounds) {
@@ -423,7 +413,7 @@
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore z-layering, position and stack crop until client has a chance to modify it.
- t.setLayer(animationLeash, mRecord.mActivityRecord.getPrefixOrderIndex());
+ t.setLayer(animationLeash, mRecord.mWindowContainer.getPrefixOrderIndex());
if (mRecord.mStartBounds != null) {
t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
@@ -464,7 +454,7 @@
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("token="); pw.println(mRecord.mActivityRecord);
+ pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);
if (mRecord.mTarget != null) {
pw.print(prefix); pw.println("Target:");
mRecord.mTarget.dump(pw, prefix + " ");
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 9db6dc2..51a3e720 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1154,7 +1154,7 @@
// activity is started and resumed, and no recursion occurs.
final ActivityStack focusedStack = display.getFocusedStack();
if (focusedStack != null) {
- focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+ result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 17f5abd..f5d3aff 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -787,7 +787,7 @@
}
}
- if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppAnimating()) {
+ if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) {
// We have finished the animation of an app transition. To do this, we have
// delayed a lot of operations like showing and hiding apps, moving apps in
// Z-order, etc.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bf7dd57..92ff2dc 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -28,13 +28,14 @@
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
import static com.android.server.wm.TaskProto.BOUNDS;
-import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -50,6 +51,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -204,7 +206,7 @@
// No reason to defer removal of a Task that doesn't have any child.
return false;
}
- return hasWindowsAlive() && mStack.isSelfOrChildAnimating();
+ return hasWindowsAlive() && mStack.isAnimating(TRANSITION | CHILDREN);
}
@Override
@@ -645,6 +647,18 @@
return getAppAnimationLayer(ANIMATION_LAYER_HOME);
}
+ boolean shouldAnimate() {
+ // Don't animate while the task runs recents animation but only if we are in the mode
+ // where we cancel with deferred screenshot, which means that the controller has
+ // transformed the task.
+ final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
+ if (controller != null && controller.isAnimatingTask(this)
+ && controller.shouldDeferCancelUntilNextTransition()) {
+ return false;
+ }
+ return true;
+ }
+
@Override
SurfaceControl.Builder makeSurface() {
return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
@@ -660,6 +674,22 @@
return false;
}
+ /**
+ * @return {@code true} if changing app transition is running.
+ */
+ @Override
+ boolean isChangingAppTransition() {
+ final ActivityRecord activity = getTopVisibleActivity();
+ return activity != null && getDisplayContent().mChangingApps.contains(activity);
+ }
+
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final ActivityRecord activity = getTopVisibleActivity();
+ return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+ }
+
WindowState getTopVisibleAppMainWindow() {
final ActivityRecord activity = getTopVisibleActivity();
return activity != null ? activity.findMainWindow() : null;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 3552245..8f8c7e7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -52,6 +52,8 @@
import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
import static com.android.server.wm.StackProto.TASKS;
import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -69,6 +71,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
@@ -934,7 +937,7 @@
@Override
void removeIfPossible() {
- if (isSelfOrChildAnimating()) {
+ if (isAnimating(TRANSITION | CHILDREN)) {
mDeferRemoval = true;
return;
}
@@ -1792,7 +1795,7 @@
/** Returns true if a removal action is still being deferred. */
boolean checkCompleteDeferredRemoval() {
- if (isSelfOrChildAnimating()) {
+ if (isAnimating(TRANSITION | CHILDREN)) {
return true;
}
if (mDeferRemoval) {
@@ -1866,4 +1869,11 @@
AnimatingActivityRegistry getAnimatingActivityRegistry() {
return mAnimatingActivityRegistry;
}
+
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final Task task = getTopChild();
+ return task != null ? task.createRemoteAnimationTarget(record) : null;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1e13aef..3632284 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -24,6 +24,8 @@
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
@@ -118,7 +120,8 @@
}
mFindResults.resetTopWallpaper = true;
- if (w.mActivityRecord != null && w.mActivityRecord.isHidden() && !w.mActivityRecord.isSelfAnimating()) {
+ if (w.mActivityRecord != null && w.mActivityRecord.isHidden()
+ && !w.mActivityRecord.isAnimating(TRANSITION)) {
// If this window's app token is hidden and not animating, it is of no interest to us.
if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
@@ -136,7 +139,7 @@
}
final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null
- && w.mActivityRecord.isSelfAnimating()
+ && w.mActivityRecord.isAnimating(TRANSITION)
&& AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit())
&& (w.mActivityRecord.getTransitFlags()
& TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
@@ -159,7 +162,8 @@
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
- final boolean animationWallpaper = w.mActivityRecord != null && w.mActivityRecord.getAnimation() != null
+ final boolean animationWallpaper = w.mActivityRecord != null
+ && w.mActivityRecord.getAnimation() != null
&& w.mActivityRecord.getAnimation().getShowWallpaper();
final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
|| animationWallpaper;
@@ -173,7 +177,7 @@
&& (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
- if (w == mWallpaperTarget && w.isAnimating()) {
+ if (w == mWallpaperTarget && w.isAnimating(TRANSITION | PARENTS)) {
// The current wallpaper target is animating, so we'll look behind it for
// another possible target and figure out what is going on later.
if (DEBUG_WALLPAPER) Slog.v(TAG,
@@ -224,19 +228,19 @@
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ " animating=" + ((wallpaperTarget != null && wallpaperTarget.mActivityRecord != null)
- ? wallpaperTarget.mActivityRecord.isSelfAnimating() : null)
+ ? wallpaperTarget.mActivityRecord.isAnimating(TRANSITION) : null)
+ " prev=" + mPrevWallpaperTarget
+ " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
return (wallpaperTarget != null
&& (!wallpaperTarget.mObscured
|| isAnimatingWithRecentsComponent
|| (wallpaperTarget.mActivityRecord != null
- && wallpaperTarget.mActivityRecord.isSelfAnimating())))
+ && wallpaperTarget.mActivityRecord.isAnimating(TRANSITION))))
|| mPrevWallpaperTarget != null;
}
boolean isWallpaperTargetAnimating() {
- return mWallpaperTarget != null && mWallpaperTarget.isAnimating()
+ return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS)
&& (mWallpaperTarget.mActivityRecord == null
|| !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart());
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 3a1d6e0..f7525a9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -192,7 +194,7 @@
mService.mWindowPlacerLocked.requestTraversal();
}
- final boolean rootAnimating = mService.mRoot.isSelfOrChildAnimating();
+ final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
if (rootAnimating && !mLastRootAnimating) {
// Usually app transitions but quite a load onto the system already (with all the
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a620a7c..7ce2b5e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -24,8 +24,15 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.SurfaceControl.Transaction;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
@@ -33,7 +40,9 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
import android.annotation.IntDef;
@@ -45,17 +54,24 @@
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
+import android.os.Trace;
+import android.util.Pair;
import android.util.Pools;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
+import android.view.WindowManager;
+import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.Animatable;
import java.io.PrintWriter;
@@ -155,6 +171,29 @@
private final Configuration mTmpConfig = new Configuration();
+ /** Interface for {@link #isAnimating} to check which cases for the container is animating. */
+ public interface AnimationFlags {
+ /**
+ * A bit flag indicates that {@link #isAnimating} should also return {@code true}
+ * even though the container is not yet animating, but the window container or its
+ * relatives as specified by PARENTS or CHILDREN are part of an {@link AppTransition}
+ * that is pending so an animation starts soon.
+ */
+ int TRANSITION = 1;
+
+ /**
+ * A bit flag indicates that {@link #isAnimating} should also check if one of the
+ * ancestors of the container are animating in addition to the container itself.
+ */
+ int PARENTS = 2;
+
+ /**
+ * A bit flag indicates that {@link #isAnimating} should also check if one of the
+ * descendants of the container are animating in addition to the container itself.
+ */
+ int CHILDREN = 4;
+ }
+
/**
* Callback which is triggered while changing the parent, after setting up the surface but
* before asking the parent to assign child layers.
@@ -163,6 +202,47 @@
void onPreAssignChildLayers();
}
+ /**
+ * True if this an AppWindowToken and the activity which created this was launched with
+ * ActivityOptions.setLaunchTaskBehind.
+ *
+ * TODO(b/142617871): We run a special animation when the activity was launched with that
+ * flag, but it's not necessary anymore. Keep the window invisible until the task is explicitly
+ * selected to suppress an animation, and remove this flag.
+ */
+ boolean mLaunchTaskBehind;
+
+ /**
+ * If we are running an animation, this determines the transition type. Must be one of
+ * {@link AppTransition#TransitionFlags}.
+ */
+ int mTransit;
+
+ /**
+ * If we are running an animation, this determines the flags during this animation. Must be a
+ * bitwise combination of AppTransition.TRANSIT_FLAG_* constants.
+ */
+ int mTransitFlags;
+
+ /** Whether this container should be boosted at the top of all its siblings. */
+ @VisibleForTesting boolean mNeedsZBoost;
+
+ /** Layer used to constrain the animation to a container's stack bounds. */
+ SurfaceControl mAnimationBoundsLayer;
+
+ /** Whether this container needs to create mAnimationBoundsLayer for cropping animations. */
+ boolean mNeedsAnimationBoundsLayer;
+
+ /**
+ * This gets used during some open/close transitions as well as during a change transition
+ * where it represents the starting-state snapshot.
+ */
+ AppWindowThumbnail mThumbnail;
+ final Rect mTransitStartRect = new Rect();
+ final Point mTmpPoint = new Point();
+ protected final Rect mTmpRect = new Rect();
+ final Rect mTmpPrevBounds = new Rect();
+
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
@@ -642,51 +722,78 @@
}
/**
- * @return Whether our own container is running an animation or any child, no matter how deep in
- * the hierarchy, is animating.
+ * @return {@code true} when this container or its related containers are running an
+ * animation, {@code false} otherwise.
+ *
+ * By default this predicate only checks if this container itself is actually running an
+ * animation, but you can extend the check target over its relatives, or relax the condition
+ * so that this can return {@code true} if an animation starts soon by giving a combination
+ * of {@link #AnimationFlags}.
+ *
+ * Note that you can give a combination of bitmask flags to specify targets and condition for
+ * checking animating status.
+ * e.g. {@code isAnimating(TRANSITION | PARENT)} returns {@code true} if either this
+ * container itself or one of its parents is running an animation or waiting for an app
+ * transition.
+ *
+ * Note that TRANSITION propagates to parents and children as well.
+ *
+ * {@see AnimationFlags#TRANSITION}
+ * {@see AnimationFlags#PARENTS}
+ * {@see AnimationFlags#CHILDREN}
*/
- boolean isSelfOrChildAnimating() {
- if (isSelfAnimating()) {
+ final boolean isAnimating(int flags) {
+ if (mSurfaceAnimator.isAnimating()) {
return true;
}
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- final WindowContainer wc = mChildren.get(j);
- if (wc.isSelfOrChildAnimating()) {
+ if ((flags & TRANSITION) != 0 && isWaitingForTransitionStart()) {
+ return true;
+ }
+ if ((flags & PARENTS) != 0) {
+ final WindowContainer parent = getParent();
+ if (parent != null && parent.isAnimating(flags & ~CHILDREN)) {
return true;
}
}
+ if ((flags & CHILDREN) != 0) {
+ for (int i = 0; i < mChildren.size(); ++i) {
+ final WindowContainer wc = mChildren.get(i);
+ if (wc.isAnimating(flags & ~PARENTS)) {
+ return true;
+ }
+ }
+ }
return false;
}
/**
- * @return Whether our own container is running an animation or our parent is animating. This
- * doesn't consider whether children are animating.
+ * @return {@code true} when the container is waiting the app transition start, {@code false}
+ * otherwise.
*/
- boolean isAnimating() {
-
- // We are animating if we ourselves are animating or if our parent is animating.
- return isSelfAnimating() || mParent != null && mParent.isAnimating();
+ boolean isWaitingForTransitionStart() {
+ return false;
}
/**
- * @return {@code true} if in this subtree of the hierarchy we have an {@link AppWindowToken}
- * that is {@link #isSelfAnimating}; {@code false} otherwise.
+ * @return {@code true} if in this subtree of the hierarchy we have an
+ * {@ode ActivityRecord#isAnimating(TRANSITION)}, {@code false} otherwise.
*/
- boolean isAppAnimating() {
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- final WindowContainer wc = mChildren.get(j);
- if (wc.isAppAnimating()) {
- return true;
- }
- }
- return false;
+ boolean isAppTransitioning() {
+ return forAllActivities(app -> app.isAnimating(TRANSITION));
}
/**
* @return Whether our own container running an animation at the moment.
*/
- boolean isSelfAnimating() {
- return mSurfaceAnimator.isAnimating();
+ final boolean isAnimating() {
+ return isAnimating(0 /* self only */);
+ }
+
+ /**
+ * @return {@code true} if the container is in changing app transition.
+ */
+ boolean isChangingAppTransition() {
+ return false;
}
void sendAppVisibilityToClients() {
@@ -988,10 +1095,13 @@
wrapper.release();
}
- void forAllActivities(Consumer<ActivityRecord> callback) {
+ boolean forAllActivities(ToBooleanFunction<ActivityRecord> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
- mChildren.get(i).forAllActivities(callback);
+ if (mChildren.get(i).forAllActivities(callback)) {
+ return true;
+ }
}
+ return false;
}
void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
@@ -1374,6 +1484,203 @@
return null;
}
+ // TODO: Remove this and use #getBounds() instead once we set an app transition animation
+ // on TaskStack.
+ Rect getAnimationBounds(int appStackClipMode) {
+ return getBounds();
+ }
+
+ /**
+ * Applies the app transition animation according the given the layout properties in the
+ * window hierarchy.
+ *
+ * @param lp The layout parameters of the window.
+ * @param transit The app transition type indicates what kind of transition to be applied.
+ * @param enter Whether the app transition is entering transition or not.
+ * @param isVoiceInteraction Whether the container is participating in voice interaction or not.
+ *
+ * @return {@code true} when the container applied the app transition, {@code false} if the
+ * app transition is disabled or skipped.
+ *
+ * @see #getAnimationAdapter
+ */
+ boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+ boolean isVoiceInteraction) {
+ if (mWmService.mDisableTransitionAnimation) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: transition animation is disabled or skipped. "
+ + "container=%s", this);
+ cancelAnimation();
+ return false;
+ }
+
+ // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
+ // to animate and it can cause strange artifacts when we unfreeze the display if some
+ // different animation is running.
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
+ if (okToAnimate()) {
+ Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, transit,
+ enter, isVoiceInteraction);
+ AnimationAdapter adapter = adapters.first;
+ AnimationAdapter thumbnailAdapter = adapters.second;
+ if (adapter != null) {
+ startAnimation(getPendingTransaction(), adapter, !isVisible());
+ if (adapter.getShowWallpaper()) {
+ mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ if (thumbnailAdapter != null) {
+ mThumbnail.startAnimation(
+ getPendingTransaction(), thumbnailAdapter, !isVisible());
+ }
+ }
+ } else {
+ cancelAnimation();
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ return isAnimating();
+ }
+
+ /**
+ * Gets the {@link AnimationAdapter} according the given window layout properties in the window
+ * hierarchy.
+ *
+ * @return The return value will always contain two elements, one for normal animations and the
+ * other for thumbnail animation, both can be {@code null}.
+ *
+ * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
+ * @See LocalAnimationAdapter
+ */
+ Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
+ int transit, boolean enter, boolean isVoiceInteraction) {
+ final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
+ final int appStackClipMode = getDisplayContent().mAppTransition.getAppStackClipMode();
+
+ // Separate position and size for use in animators.
+ mTmpRect.set(getAnimationBounds(appStackClipMode));
+ mTmpPoint.set(mTmpRect.left, mTmpRect.top);
+ mTmpRect.offsetTo(0, 0);
+
+ final RemoteAnimationController controller =
+ getDisplayContent().mAppTransition.getRemoteAnimationController();
+ final boolean isChanging = AppTransition.isChangeTransit(transit) && enter
+ && isChangingAppTransition();
+
+ // Delaying animation start isn't compatible with remote animations at all.
+ if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
+ final RemoteAnimationController.RemoteAnimationRecord adapters =
+ controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
+ (isChanging ? mTransitStartRect : null));
+ resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
+ } else if (isChanging) {
+ final float durationScale = mWmService.getTransitionAnimationScaleLocked();
+ final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
+ mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
+
+ AnimationAdapter adapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo,
+ durationScale, true /* isAppAnimation */, false /* isThumbnail */),
+ getSurfaceAnimationRunner());
+
+ AnimationAdapter thumbnailAdapter = null;
+ if (mThumbnail != null) {
+ thumbnailAdapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo,
+ durationScale, true /* isAppAnimation */, true /* isThumbnail */),
+ getSurfaceAnimationRunner());
+ }
+ resultAdapters = new Pair<>(adapter, thumbnailAdapter);
+ mTransit = transit;
+ mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
+ } else {
+ mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
+ final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
+
+ if (a != null) {
+ // Only apply corner radius to animation if we're not in multi window mode.
+ // We don't want rounded corners when in pip or split screen.
+ final float windowCornerRadius = !inMultiWindowMode()
+ ? getDisplayContent().getWindowCornerRadius()
+ : 0;
+ AnimationAdapter adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
+ getDisplayContent().mAppTransition.canSkipFirstFrame(),
+ appStackClipMode, true /* isAppAnimation */, windowCornerRadius),
+ getSurfaceAnimationRunner());
+
+ resultAdapters = new Pair<>(adapter, null);
+ mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP;
+ mTransit = transit;
+ mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
+ } else {
+ resultAdapters = new Pair<>(null, null);
+ }
+ }
+ return resultAdapters;
+ }
+
+ final SurfaceAnimationRunner getSurfaceAnimationRunner() {
+ return mWmService.mSurfaceAnimationRunner;
+ }
+
+ private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+ boolean isVoiceInteraction) {
+ final DisplayContent displayContent = getDisplayContent();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int width = displayInfo.appWidth;
+ final int height = displayInfo.appHeight;
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: container=%s", this);
+
+ // Determine the visible rect to calculate the thumbnail clip with
+ // getAnimationFrames.
+ final Rect frame = new Rect(0, 0, width, height);
+ final Rect displayFrame = new Rect(0, 0,
+ displayInfo.logicalWidth, displayInfo.logicalHeight);
+ final Rect insets = new Rect();
+ final Rect stableInsets = new Rect();
+ final Rect surfaceInsets = new Rect();
+ getAnimationFrames(frame, insets, stableInsets, surfaceInsets);
+
+ if (mLaunchTaskBehind) {
+ // Differentiate the two animations. This one which is briefly on the screen
+ // gets the !enter animation, and the other one which remains on the
+ // screen gets the enter animation. Both appear in the mOpeningApps set.
+ enter = false;
+ }
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
+ + "surfaceInsets=%s",
+ AppTransition.appTransitionToString(transit), enter, frame, insets, surfaceInsets);
+ final Configuration displayConfig = displayContent.getConfiguration();
+ final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
+ displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
+ surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this);
+ if (a != null) {
+ if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+ final int containingWidth = frame.width();
+ final int containingHeight = frame.height();
+ a.initialize(containingWidth, containingHeight, width, height);
+ a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
+ }
+ return a;
+ }
+
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ return null;
+ }
+
+ boolean okToDisplay() {
+ return mDisplayContent != null && mDisplayContent.okToDisplay();
+ }
+
+ boolean okToAnimate() {
+ return mDisplayContent != null && mDisplayContent.okToAnimate();
+ }
+
@Override
public void commitPendingTransaction() {
scheduleAnimation();
@@ -1473,6 +1780,26 @@
return getBounds();
}
+ /**
+ * The {@code outFrame} retrieved by this method specifies where the animation will finish
+ * the entrance animation, as the next frame will display the window at these coordinates. In
+ * case of exit animation, this is where the animation will start, as the frame before the
+ * animation is displaying the window at these bounds.
+ *
+ * @param outFrame The bounds where entrance animation finishes or exit animation starts.
+ * @param outInsets Insets that are covered by system windows.
+ * @param outStableInsets Insets that determine the area covered by the stable system windows.
+ * @param outSurfaceInsets Positive insets between the drawing surface and window content.
+ */
+ void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
+ Rect outSurfaceInsets) {
+ final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
+ outFrame.set(0, 0, displayInfo.appWidth, displayInfo.appHeight);
+ outInsets.setEmpty();
+ outStableInsets.setEmpty();
+ outSurfaceInsets.setEmpty();
+ }
+
void getRelativeDisplayedPosition(Point outPos) {
final Rect dispBounds = getDisplayedBounds();
outPos.set(dispBounds.left, dispBounds.top);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 085b0f6..ab937e0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -94,6 +94,9 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.ProtoLogGroup.WM_ERROR;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
@@ -2418,7 +2421,7 @@
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = true;
win.mAnimatingExit = true;
- } else if (win.isAnimating()) {
+ } else if (win.isAnimating(TRANSITION | PARENTS)) {
// Currently in a hide animation... turn this into
// an exit.
win.mAnimatingExit = true;
@@ -7618,7 +7621,7 @@
private void waitForAnimationsToComplete() {
synchronized (mGlobalLock) {
long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS;
- while (mRoot.isSelfOrChildAnimating() && timeoutRemaining > 0) {
+ while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) {
long startTime = System.currentTimeMillis();
try {
mGlobalLock.wait(timeoutRemaining);
@@ -7627,7 +7630,7 @@
timeoutRemaining -= (System.currentTimeMillis() - startTime);
}
- if (mRoot.isSelfOrChildAnimating()) {
+ if (mRoot.isAnimating(TRANSITION | CHILDREN)) {
Log.w(TAG, "Timed out waiting for animations to complete.");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f0ea13d..f7402e1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -98,6 +98,8 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RESIZE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
@@ -1492,7 +1494,8 @@
@Override
boolean hasContentToDisplay() {
if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE
- || (isAnimating() && !getDisplayContent().mAppTransition.isTransitionSet()))) {
+ || (isAnimating(TRANSITION | PARENTS)
+ && !getDisplayContent().mAppTransition.isTransitionSet()))) {
return true;
}
@@ -1550,8 +1553,8 @@
*/
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
boolean isWinVisibleLw() {
- return (mActivityRecord == null || !mActivityRecord.hiddenRequested || mActivityRecord.isSelfAnimating())
- && isVisible();
+ return (mActivityRecord == null || !mActivityRecord.hiddenRequested
+ || mActivityRecord.isAnimating(TRANSITION)) && isVisible();
}
/**
@@ -1597,9 +1600,9 @@
final ActivityRecord atoken = mActivityRecord;
if (atoken != null) {
return ((!isParentWindowHidden() && !atoken.hiddenRequested)
- || isAnimating());
+ || isAnimating(TRANSITION | PARENTS));
}
- return !isParentWindowHidden() || isAnimating();
+ return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
}
/**
@@ -1634,7 +1637,7 @@
final boolean parentAndClientVisible = !isParentWindowHidden()
&& mViewVisibility == View.VISIBLE && !mToken.isHidden();
return mHasSurface && isVisibleByPolicy() && !mDestroying
- && (parentAndClientVisible || isAnimating());
+ && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}
// TODO: Another visibility method that was added late in the release to minimize risk.
@@ -1664,7 +1667,7 @@
final ActivityRecord atoken = mActivityRecord;
return isDrawnLw() && isVisibleByPolicy()
&& ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
- || isAnimating());
+ || isAnimating(TRANSITION | PARENTS));
}
/**
@@ -1672,7 +1675,7 @@
*/
@Override
public boolean isAnimatingLw() {
- return isAnimating();
+ return isAnimating(TRANSITION | PARENTS);
}
@Override
@@ -1718,7 +1721,7 @@
// to determine if it's occluding apps.
return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
|| (mIsWallpaper && mWallpaperVisible))
- && isDrawnLw() && !isAnimating();
+ && isDrawnLw() && !isAnimating(TRANSITION | PARENTS);
}
@Override
@@ -1740,7 +1743,7 @@
// Starting window that's exiting will be removed when the animation finishes.
// Mark all relevant flags for that onExitAnimationDone will proceed all the way
// to actually remove it.
- if (!visible && isVisibleNow() && mActivityRecord.isSelfAnimating()) {
+ if (!visible && isVisibleNow() && mActivityRecord.isAnimating(TRANSITION)) {
mAnimatingExit = true;
mRemoveOnExit = true;
mWindowRemovalAllowed = true;
@@ -2016,8 +2019,10 @@
+ "mWillReplaceWindow=%b inPendingTransaction=%b mDisplayFrozen=%b "
+ "callers=%s",
this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
- mHasSurface, mWinAnimator.getShown(), isAnimating(),
- mActivityRecord != null && mActivityRecord.isSelfAnimating(), mWillReplaceWindow,
+ mHasSurface, mWinAnimator.getShown(),
+ isAnimating(TRANSITION | PARENTS),
+ mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION),
+ mWillReplaceWindow,
mActivityRecord != null && mActivityRecord.inPendingTransaction,
mWmService.mDisplayFrozen, Debug.getCallers(6));
@@ -2076,7 +2081,7 @@
mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
- final boolean isAnimating = isAnimating()
+ final boolean isAnimating = isAnimating(TRANSITION | PARENTS)
&& (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart());
final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
&& mActivityRecord.isLastWindow(this);
@@ -2683,10 +2688,11 @@
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
- + isLegacyPolicyVisibility() + " animating=" + isAnimating());
+ + isLegacyPolicyVisibility()
+ + " animating=" + isAnimating(TRANSITION | PARENTS));
if (!mToken.okToAnimate()) {
doAnimation = false;
- } else if (isLegacyPolicyVisibility() && !isAnimating()) {
+ } else if (isLegacyPolicyVisibility() && !isAnimating(TRANSITION | PARENTS)) {
// Check for the case where we are currently visible and
// not animating; we do not want to do animation at such a
// point to become visible when we already are.
@@ -2726,7 +2732,7 @@
}
if (doAnimation) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
- if (!isAnimating()) {
+ if (!isAnimating(TRANSITION | PARENTS)) {
doAnimation = false;
}
}
@@ -4163,9 +4169,9 @@
+ " tok.hiddenRequested="
+ (mActivityRecord != null && mActivityRecord.hiddenRequested)
+ " tok.hidden=" + (mActivityRecord != null && mActivityRecord.isHidden())
- + " animating=" + isAnimating()
+ + " animating=" + isAnimating(TRANSITION | PARENTS)
+ " tok animating="
- + (mActivityRecord != null && mActivityRecord.isSelfAnimating())
+ + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION))
+ " Callers=" + Debug.getCallers(4));
}
}
@@ -4392,7 +4398,7 @@
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
- + " selfAnimating=" + isSelfAnimating());
+ + " selfAnimating=" + isAnimating());
if (!mChildren.isEmpty()) {
// Copying to a different list as multiple children can be removed.
@@ -4415,7 +4421,7 @@
}
}
- if (isSelfAnimating()) {
+ if (isAnimating()) {
return;
}
if (mWmService.mAccessibilityController != null) {
@@ -4563,25 +4569,25 @@
}
if (DEBUG_VISIBILITY) {
Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw()
- + ", animating=" + isAnimating());
+ + ", animating=" + isAnimating(TRANSITION | PARENTS));
if (!isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
+ " pv=" + isVisibleByPolicy()
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
+ " th=" + (mActivityRecord != null ? mActivityRecord.hiddenRequested : false)
- + " a=" + isAnimating());
+ + " a=" + isAnimating(TRANSITION | PARENTS));
}
}
results.numInteresting++;
if (isDrawnLw()) {
results.numDrawn++;
- if (!isAnimating()) {
+ if (!isAnimating(TRANSITION | PARENTS)) {
results.numVisible++;
}
results.nowGone = false;
- } else if (isAnimating()) {
+ } else if (isAnimating(TRANSITION | PARENTS)) {
results.nowGone = false;
}
}
@@ -4718,7 +4724,7 @@
+ mRemoveOnExit + ", mDestroying=" + mDestroying);
// Cancel the existing exit animation for the next enter animation.
- if (isSelfAnimating()) {
+ if (isAnimating()) {
cancelAnimation();
destroySurfaceUnchecked();
}
@@ -5398,4 +5404,29 @@
}
return mKeyInterceptionInfo;
}
+
+ @Override
+ void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
+ Rect outSurfaceInsets) {
+ // Containing frame will usually cover the whole screen, including dialog windows.
+ // For freeform workspace windows it will not cover the whole screen and it also
+ // won't exactly match the final freeform window frame (e.g. when overlapping with
+ // the status bar). In that case we need to use the final frame.
+ if (inFreeformWindowingMode()) {
+ outFrame.set(getFrameLw());
+ } else if (isLetterboxedAppWindow()) {
+ outFrame.set(getTask().getBounds());
+ } else if (isDockedResizing()) {
+ // If we are animating while docked resizing, then use the stack bounds as the
+ // animation target (which will be different than the task bounds)
+ outFrame.set(getTask().getParent().getBounds());
+ } else {
+ outFrame.set(getContainingFrame());
+ }
+ outSurfaceInsets.set(getAttrs().surfaceInsets);
+ // TODO(b/72757033): These are insets relative to the window frame, but we're really
+ // interested in the insets relative to the frame we chose in the if-blocks above.
+ getContentInsets(outInsets);
+ getStableInsets(outStableInsets);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3f25f89..fef3a9d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -32,6 +32,8 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
@@ -435,7 +437,7 @@
return;
}
- if (!mWin.mActivityRecord.isSelfAnimating()) {
+ if (!mWin.mActivityRecord.isAnimating(TRANSITION)) {
mWin.mActivityRecord.clearAllDrawn();
} else {
// Currently animating, persist current state of allDrawn until animation
@@ -1169,7 +1171,7 @@
w.mToken.hasVisible = true;
}
} else {
- if (DEBUG_ANIM && mWin.isAnimating()) {
+ if (DEBUG_ANIM && mWin.isAnimating(TRANSITION | PARENTS)) {
Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
displayed = true;
@@ -1332,7 +1334,7 @@
* @return true if an animation has been loaded.
*/
boolean applyAnimationLocked(int transit, boolean isEntrance) {
- if (mWin.isSelfAnimating() && mAnimationIsEntrance == isEntrance) {
+ if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
// an animation of the same type, then just leave that one alone.
return true;
@@ -1400,7 +1402,7 @@
mWin.getDisplayContent().adjustForImeIfNeeded();
}
- return mWin.isAnimating();
+ return mWin.isAnimating(TRANSITION | PARENTS);
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index ad71237..88a1458 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -22,6 +22,9 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -159,13 +162,10 @@
final int count = mChildren.size();
boolean changed = false;
- boolean delayed = false;
+ final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN);
for (int i = 0; i < count; i++) {
final WindowState win = mChildren.get(i);
- if (win.isAnimating()) {
- delayed = true;
- }
changed |= win.onSetAppExiting();
}
@@ -321,14 +321,6 @@
return toString();
}
- boolean okToDisplay() {
- return mDisplayContent != null && mDisplayContent.okToDisplay();
- }
-
- boolean okToAnimate() {
- return mDisplayContent != null && mDisplayContent.okToAnimate();
- }
-
/**
* Return whether windows from this token can layer above the
* system bars, or in other words extend outside of the "Decor Frame"
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5883048..2009dbd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -27,6 +27,7 @@
import android.annotation.NonNull;
import android.annotation.StringRes;
import android.app.ActivityThread;
+import android.app.AppCompatCallbacks;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -646,6 +647,7 @@
ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
new PlatformCompatNative(platformCompat));
+ AppCompatCallbacks.install(new long[0]);
t.traceEnd();
// Wait for installd to finish starting up so that it has a chance to
@@ -1133,7 +1135,6 @@
StatusBarManagerService statusBar = null;
INotificationManager notification = null;
- LocationManagerService location = null;
CountryDetectorService countryDetector = null;
ILockSettings lockSettings = null;
MediaRouterService mediaRouter = null;
@@ -1430,12 +1431,7 @@
t.traceEnd();
t.traceBegin("StartLocationManagerService");
- try {
- location = new LocationManagerService(context);
- ServiceManager.addService(Context.LOCATION_SERVICE, location);
- } catch (Throwable e) {
- reportWtf("starting Location Manager", e);
- }
+ mSystemServiceManager.startService(LocationManagerService.Lifecycle.class);
t.traceEnd();
t.traceBegin("StartCountryDetectorService");
@@ -2019,7 +2015,6 @@
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
- final LocationManagerService locationF = location;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
final InputManagerService inputManagerF = inputManager;
@@ -2175,16 +2170,6 @@
}
t.traceEnd();
-
- t.traceBegin("MakeLocationServiceReady");
- try {
- if (locationF != null) {
- locationF.systemRunning();
- }
- } catch (Throwable e) {
- reportWtf("Notifying Location Service running", e);
- }
- t.traceEnd();
t.traceBegin("MakeCountryDetectionServiceReady");
try {
if (countryDetectorF != null) {
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 2ab8189..e24dec5 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,7 +1,7 @@
java_library_static {
name: "services.net",
srcs: [
- ":tethering-services-srcs",
+ ":tethering-servicesnet-srcs",
"java/**/*.java",
],
static_libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 9e7b805..485f436 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -38,7 +38,6 @@
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
-import static com.android.server.AlarmManagerService.Constants.KEY_APP_STANDBY_QUOTAS_ENABLED;
import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
@@ -305,6 +304,8 @@
argThat((filter) -> filter.hasAction(BatteryManager.ACTION_CHARGING)
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
mChargingReceiver = chargingReceiverCaptor.getValue();
+
+ setTestableQuotas();
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
@@ -342,9 +343,10 @@
}
/**
+ * Lowers quotas to make testing feasible.
* Careful while calling as this will replace any existing settings for the calling test.
*/
- private void setQuotasEnabled(boolean enabled) {
+ private void setTestableQuotas() {
final StringBuilder constantsBuilder = new StringBuilder();
constantsBuilder.append(KEY_MIN_FUTURITY);
constantsBuilder.append("=0,");
@@ -353,14 +355,9 @@
constantsBuilder.append("=8,");
constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]);
constantsBuilder.append("=5,");
- if (!enabled) {
- constantsBuilder.append(KEY_APP_STANDBY_QUOTAS_ENABLED);
- constantsBuilder.append("=false,");
- }
doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver,
Settings.Global.ALARM_MANAGER_CONSTANTS));
mService.mConstants.onChange(false, null);
- assertEquals(mService.mConstants.APP_STANDBY_QUOTAS_ENABLED, enabled);
}
@Test
@@ -481,67 +478,6 @@
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
}
- @Test
- public void testStandbyBucketDelay_workingSet() throws Exception {
- setQuotasEnabled(false);
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
- assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
-
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
-
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
-
- verify(mUsageStatsManagerInternal, atLeastOnce())
- .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
- eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
- final long expectedNextTrigger = mNowElapsedTest
- + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET);
- assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
- }
-
- @Test
- public void testStandbyBucketDelay_frequent() throws Exception {
- setQuotasEnabled(false);
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
- assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
-
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
-
- verify(mUsageStatsManagerInternal, atLeastOnce())
- .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
- eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
- final long expectedNextTrigger = mNowElapsedTest
- + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT);
- assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
- }
-
- @Test
- public void testStandbyBucketDelay_rare() throws Exception {
- setQuotasEnabled(false);
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
- assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
-
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(STANDBY_BUCKET_RARE);
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
-
- verify(mUsageStatsManagerInternal, atLeastOnce())
- .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
- eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
- final long expectedNextTrigger = mNowElapsedTest
- + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE);
- assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
- }
-
private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
final int quota = mService.getQuotaForBucketLocked(standbyBucket);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
@@ -601,73 +537,61 @@
@Test
public void testActiveQuota_deferredOnSet() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_deferredOnExpiration() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_notDeferred() throws Exception {
- setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testWorkingQuota_deferredOnSet() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_deferredOnExpiration() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_notDeferred() throws Exception {
- setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testFrequentQuota_deferredOnSet() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_deferredOnExpiration() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_notDeferred() throws Exception {
- setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testRareQuota_deferredOnSet() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_deferredOnExpiration() throws Exception {
- setQuotasEnabled(true);
testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_notDeferred() throws Exception {
- setQuotasEnabled(true);
testQuotasNoDeferral(STANDBY_BUCKET_RARE);
}
@@ -686,7 +610,6 @@
@Test
public void testQuotaDowngrade() throws Exception {
- setQuotasEnabled(true);
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
@@ -714,7 +637,6 @@
@Test
public void testQuotaUpgrade() throws Exception {
- setQuotasEnabled(true);
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
@@ -752,7 +674,6 @@
@Test
public void testCharging() throws Exception {
- setQuotasEnabled(true);
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 1ad7b6e..79af34d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -191,7 +191,7 @@
@After
public void tearDown() throws Exception {
mHandlerThread.quitSafely();
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(PermissionManagerServiceInternal.class);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 80439cf..a1322b9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -62,7 +62,6 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.util.ArrayMap;
@@ -162,11 +161,11 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
@@ -465,7 +464,7 @@
// Testing that in restore we are given the canonical version
loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
- verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+ verify(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
}
@Test
@@ -475,11 +474,11 @@
.appendQueryParameter("title", "Test")
.appendQueryParameter("canonical", "1")
.build();
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(canonicalBasedOnLocal);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(localUri);
- when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal)))
.thenReturn(localUri);
NotificationChannel channel =
@@ -499,9 +498,9 @@
@Test
public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(null);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(null);
NotificationChannel channel =
@@ -526,7 +525,7 @@
@Test
public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null);
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 2abd340..8774b63 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -132,11 +132,11 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 3b336eb..aceed86 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -12,6 +12,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -109,8 +110,8 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
- eq(null), argThat(b -> {
+ verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
@@ -167,8 +168,8 @@
// Throw exception when trying to pin
doAnswer(invocation -> {
throw new Exception("Pin failed");
- }).when(mIContentProvider).call(
- anyString(), anyString(), anyString(), eq(null), any());
+ }).when(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ anyString(), eq(null), any());
TestableLooper.get(this).processAllMessages();
@@ -176,8 +177,8 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
- eq(null), argThat(b -> {
+ verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 03367db..7c867b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -149,9 +149,7 @@
public void testOnActivityLaunchFinished() {
onActivityLaunched();
- mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.elapsedRealtimeNanos());
-
+ notifyTransitionStarting();
notifyWindowsDrawn(mTopActivity);
verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -159,10 +157,10 @@
}
@Test
- public void testOnActivityLaunchCancelled() {
+ public void testOnActivityLaunchCancelled_hasDrawn() {
onActivityLaunched();
- mTopActivity.mDrawn = true;
+ mTopActivity.visible = mTopActivity.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
@@ -172,6 +170,28 @@
}
@Test
+ public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
+ mTopActivity.visible = mTopActivity.mDrawn = true;
+
+ // Suppress resume when creating the record because we want to notify logger manually.
+ mSupervisor.beginDeferResume();
+ // Create an activity with different process that meets process switch.
+ final ActivityRecord noDrawnActivity = new ActivityBuilder(mService)
+ .setTask(mTopActivity.getTaskRecord())
+ .setProcessName("other")
+ .build();
+ mSupervisor.readyToResume();
+
+ mActivityMetricsLogger.notifyActivityLaunching(noDrawnActivity.intent);
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
+
+ noDrawnActivity.destroyIfPossible("test");
+ mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity);
+
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(noDrawnActivity));
+ }
+
+ @Test
public void testOnReportFullyDrawn() {
onActivityLaunched();
@@ -184,16 +204,21 @@
private void onActivityLaunchedTrampoline() {
onIntentStarted();
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
-
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
-
- // A second, distinct, activity launch is coalesced into the the current app launch sequence
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTrampolineActivity), anyInt());
+
+ // A second, distinct, activity launch is coalesced into the current app launch sequence.
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
+
verifyNoMoreInteractions(mLaunchObserver);
}
+ private void notifyTransitionStarting() {
+ mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+ SystemClock.elapsedRealtimeNanos());
+ }
+
private void notifyWindowsDrawn(ActivityRecord r) {
mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(),
SystemClock.elapsedRealtimeNanos());
@@ -203,15 +228,12 @@
public void testOnActivityLaunchFinishedTrampoline() {
onActivityLaunchedTrampoline();
- mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.elapsedRealtimeNanos());
-
+ notifyTransitionStarting();
notifyWindowsDrawn(mTrampolineActivity);
notifyWindowsDrawn(mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTrampolineActivity),
- anyLong());
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -219,12 +241,12 @@
public void testOnActivityLaunchCancelledTrampoline() {
onActivityLaunchedTrampoline();
- mTrampolineActivity.mDrawn = true;
+ mTopActivity.mDrawn = true;
// Cannot time already-visible activities.
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTrampolineActivity);
+ mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTrampolineActivity));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
verifyNoMoreInteractions(mLaunchObserver);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 99ecdcb..38d6c9c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -836,6 +836,14 @@
// Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() -
// this will cause NPE when updating task's process.
mActivity.app = null;
+
+ // Put a visible activity on top, so the finishing activity doesn't have to wait until the
+ // next activity reports idle to destroy it.
+ final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.visible = true;
+ topActivity.nowVisible = true;
+ topActivity.setState(RESUMED, "test");
+
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED,
mActivity.finishIfPossible("test", false /* oomAdj */));
assertTrue(mActivity.finishing);
@@ -1131,8 +1139,11 @@
// Add another stack to become focused and make the activity there visible. This way it
// simulates finishing in non-focused stack in split-screen.
final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
- stack.getChildAt(0).getChildAt(0).nowVisible = true;
- stack.getChildAt(0).getChildAt(0).visible = true;
+ final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0);
+ focusedActivity.nowVisible = true;
+ focusedActivity.visible = true;
+ focusedActivity.setState(RESUMED, "test");
+ stack.mResumedActivity = focusedActivity;
topActivity.completeFinishing("test");
@@ -1180,6 +1191,30 @@
}
/**
+ * Verify that complete finish request for visible activity must resume next home stack before
+ * destroying it immediately if it is the last running activity on a display with a home stack.
+ * We must wait for home activity to come up to avoid a black flash in this case.
+ */
+ @Test
+ public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
+ // Empty the home stack.
+ final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
+ for (TaskRecord t : homeStack.getAllTasks()) {
+ homeStack.removeTask(t, "test", REMOVE_TASK_MODE_DESTROYING);
+ }
+ mActivity.finishing = true;
+ spyOn(mStack);
+
+ // Try to finish the last activity above the home stack.
+ mActivity.completeFinishing("test");
+
+ // Verify that the activity is not destroyed immediately, but waits for next one to come up.
+ verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+ assertEquals(FINISHING, mActivity.getState());
+ assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
+ }
+
+ /**
* Test that the activity will be moved to destroying state and the message to destroy will be
* sent to the client.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 80f0851..78db6c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -118,7 +118,8 @@
private ComponentName mComponent;
private String mTargetActivity;
private TaskRecord mTaskRecord;
- private int mUid;
+ private String mProcessName = "name";
+ private int mUid = 12345;
private boolean mCreateTask;
private ActivityStack mStack;
private int mActivityFlags;
@@ -175,6 +176,11 @@
return this;
}
+ ActivityBuilder setProcessName(String name) {
+ mProcessName = name;
+ return this;
+ }
+
ActivityBuilder setUid(int uid) {
mUid = uid;
return this;
@@ -235,6 +241,7 @@
aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
+ aInfo.processName = mProcessName;
aInfo.packageName = mComponent.getPackageName();
if (mTargetActivity != null) {
aInfo.targetActivity = mTargetActivity;
@@ -268,7 +275,7 @@
}
final WindowProcessController wpc = new WindowProcessController(mService,
- mService.mContext.getApplicationInfo(), "name", 12345,
+ mService.mContext.getApplicationInfo(), mProcessName, mUid,
UserHandle.getUserId(12345), mock(Object.class),
mock(WindowProcessListener.class));
wpc.setThread(mock(IApplicationThread.class));
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
index 77f9f04..b6eaab7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -21,6 +21,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -69,8 +70,8 @@
activity1.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */);
activity2.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */);
- assertTrue(activity1.isSelfAnimating());
- assertTrue(activity2.isSelfAnimating());
+ assertTrue(activity1.isAnimating(TRANSITION));
+ assertTrue(activity2.isAnimating(TRANSITION));
// Make sure that first animation finish is deferred, second one is not deferred, and first
// one gets cancelled.
@@ -92,8 +93,8 @@
window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
- assertTrue(window1.isSelfAnimating());
- assertTrue(window2.isSelfAnimating());
+ assertTrue(window1.isAnimating(TRANSITION));
+ assertTrue(window2.isAnimating(TRANSITION));
// Make sure that first animation finish is deferred, and removing the second window stops
// finishes all pending deferred finishings.
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 06afce2..5f42d23 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -147,8 +147,8 @@
// Make sure each display is in animating stage.
assertTrue(dc1.mOpeningApps.size() > 0);
assertTrue(dc2.mClosingApps.size() > 0);
- assertTrue(dc1.isAppAnimating());
- assertTrue(dc2.isAppAnimating());
+ assertTrue(dc1.isAppTransitioning());
+ assertTrue(dc2.isAppTransitioning());
}
@Test
@@ -219,10 +219,10 @@
assertTrue(dc.mClosingApps.size() > 0);
// Make sure window is in animating stage before freeze, and cancel after freeze.
- assertTrue(dc.isAppAnimating());
+ assertTrue(dc.isAppTransitioning());
assertFalse(runner.mCancelled);
dc.mAppTransition.freeze();
- assertFalse(dc.isAppAnimating());
+ assertFalse(dc.isAppTransitioning());
assertTrue(runner.mCancelled);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 2fc03c7..164d28d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -23,12 +23,13 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import android.graphics.Rect;
@@ -141,7 +142,7 @@
final Task task = createTaskInStack(stack, 0 /* userId */);
// Stack removal is deferred if one of its child is animating.
- doReturn(true).when(task).isSelfAnimating();
+ doReturn(true).when(task).isAnimating(TRANSITION | CHILDREN);
stack.removeIfPossible();
// For the case of deferred removal the task controller will still be connected to the its
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 85aff7f..8536448 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -24,13 +24,18 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -335,7 +340,53 @@
}
@Test
- public void testIsAnimating() {
+ public void testIsAnimating_TransitionFlag() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = builder.setLayer(0).build();
+ final TestWindowContainer child1 = root.addChildWindow(
+ builder.setWaitForTransitionStart(true));
+
+ assertFalse(root.isAnimating(TRANSITION));
+ assertTrue(child1.isAnimating(TRANSITION));
+ }
+
+ @Test
+ public void testIsAnimating_ParentsFlag() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = builder.setLayer(0).build();
+ final TestWindowContainer child1 = root.addChildWindow(builder);
+ final TestWindowContainer child2 = root.addChildWindow(builder.setIsAnimating(true));
+ final TestWindowContainer child21 = child2.addChildWindow(builder.setIsAnimating(false));
+
+ assertFalse(root.isAnimating());
+ assertFalse(child1.isAnimating());
+ assertFalse(child1.isAnimating(PARENTS));
+ assertTrue(child2.isAnimating());
+ assertTrue(child2.isAnimating(PARENTS));
+ assertFalse(child21.isAnimating());
+ assertTrue(child21.isAnimating(PARENTS));
+ }
+
+ @Test
+ public void testIsAnimating_ChildrenFlag() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = builder.setLayer(0).build();
+ final TestWindowContainer child1 = root.addChildWindow(builder);
+ final TestWindowContainer child2 = root.addChildWindow(builder.setIsAnimating(true));
+ final TestWindowContainer child11 = child1.addChildWindow(builder.setIsAnimating(true));
+
+ assertFalse(root.isAnimating());
+ assertTrue(root.isAnimating(CHILDREN));
+ assertFalse(child1.isAnimating());
+ assertTrue(child1.isAnimating(CHILDREN));
+ assertTrue(child2.isAnimating());
+ assertTrue(child2.isAnimating(CHILDREN));
+ assertTrue(child11.isAnimating());
+ assertTrue(child11.isAnimating(CHILDREN));
+ }
+
+ @Test
+ public void testIsAnimating_combineFlags() {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
@@ -345,19 +396,19 @@
final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true));
final TestWindowContainer child21 = child2.addChildWindow();
- assertFalse(root.isAnimating());
- assertTrue(child1.isAnimating());
- assertTrue(child11.isAnimating());
- assertTrue(child12.isAnimating());
- assertFalse(child2.isAnimating());
- assertFalse(child21.isAnimating());
+ assertFalse(root.isAnimating(TRANSITION | PARENTS));
+ assertTrue(child1.isAnimating(TRANSITION | PARENTS));
+ assertTrue(child11.isAnimating(TRANSITION | PARENTS));
+ assertTrue(child12.isAnimating(TRANSITION | PARENTS));
+ assertFalse(child2.isAnimating(TRANSITION | PARENTS));
+ assertFalse(child21.isAnimating(TRANSITION | PARENTS));
- assertTrue(root.isSelfOrChildAnimating());
- assertTrue(child1.isSelfOrChildAnimating());
- assertFalse(child11.isSelfOrChildAnimating());
- assertTrue(child12.isSelfOrChildAnimating());
- assertFalse(child2.isSelfOrChildAnimating());
- assertFalse(child21.isSelfOrChildAnimating());
+ assertTrue(root.isAnimating(TRANSITION | CHILDREN));
+ assertTrue(child1.isAnimating(TRANSITION | CHILDREN));
+ assertFalse(child11.isAnimating(TRANSITION | CHILDREN));
+ assertTrue(child12.isAnimating(TRANSITION | CHILDREN));
+ assertFalse(child2.isAnimating(TRANSITION | CHILDREN));
+ assertFalse(child21.isAnimating(TRANSITION | CHILDREN));
}
@Test
@@ -716,6 +767,7 @@
private boolean mIsAnimating;
private boolean mIsVisible;
private boolean mFillsParent;
+ private boolean mWaitForTransitStart;
private Integer mOrientation;
private boolean mOnParentChangedCalled;
@@ -738,7 +790,7 @@
};
TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating,
- boolean isVisible, Integer orientation) {
+ boolean isVisible, boolean waitTransitStart, Integer orientation) {
super(wm);
mLayer = layer;
@@ -746,6 +798,9 @@
mIsVisible = isVisible;
mFillsParent = true;
mOrientation = orientation;
+ mWaitForTransitStart = waitTransitStart;
+ spyOn(mSurfaceAnimator);
+ doReturn(mIsAnimating).when(mSurfaceAnimator).isAnimating();
}
TestWindowContainer getParentWindow() {
@@ -783,11 +838,6 @@
}
@Override
- boolean isSelfAnimating() {
- return mIsAnimating;
- }
-
- @Override
boolean isVisible() {
return mIsVisible;
}
@@ -810,6 +860,11 @@
void setFillsParent(boolean fillsParent) {
mFillsParent = fillsParent;
}
+
+ @Override
+ boolean isWaitingForTransitionStart() {
+ return mWaitForTransitStart;
+ }
}
private static class TestWindowContainerBuilder {
@@ -817,6 +872,7 @@
private int mLayer;
private boolean mIsAnimating;
private boolean mIsVisible;
+ private boolean mIsWaitTransitStart;
private Integer mOrientation;
TestWindowContainerBuilder(WindowManagerService wm) {
@@ -847,8 +903,14 @@
return this;
}
+ TestWindowContainerBuilder setWaitForTransitionStart(boolean waitTransitStart) {
+ mIsWaitTransitStart = waitTransitStart;
+ return this;
+ }
+
TestWindowContainer build() {
- return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, mOrientation);
+ return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible,
+ mIsWaitTransitStart, mOrientation);
}
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index fa16b84..20abe77 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -858,7 +858,7 @@
}
/** @hide */
- public abstract static class Listener {
+ abstract static class Listener {
public void onStateChanged(Connection c, int state) {}
public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
public void onCallerDisplayNameChanged(
@@ -2006,7 +2006,7 @@
*
* @hide
*/
- public final Connection addConnectionListener(Listener l) {
+ final Connection addConnectionListener(Listener l) {
mListeners.add(l);
return this;
}
@@ -2019,7 +2019,7 @@
*
* @hide
*/
- public final Connection removeConnectionListener(Listener l) {
+ final Connection removeConnectionListener(Listener l) {
if (l != null) {
mListeners.remove(l);
}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index bc29b59..dc95f16 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -3943,10 +3944,11 @@
}
/**
- * Contains received SMS cell broadcast messages. More details are available in 3GPP TS 23.041.
+ * Contains received cell broadcast messages. More details are available in 3GPP TS 23.041.
* @hide
*/
@SystemApi
+ @TestApi
public static final class CellBroadcasts implements BaseColumns {
/**
@@ -3957,11 +3959,44 @@
/**
* The {@code content://} URI for this table.
+ * Only privileged framework components running on phone or network stack uid can
+ * query or modify this table.
*/
@NonNull
public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
/**
+ * The {@code content://} URI for query cellbroadcast message history.
+ * query results include following entries
+ * <ul>
+ * <li>{@link #_ID}</li>
+ * <li>{@link #SLOT_INDEX}</li>
+ * <li>{@link #GEOGRAPHICAL_SCOPE}</li>
+ * <li>{@link #PLMN}</li>
+ * <li>{@link #LAC}</li>
+ * <li>{@link #CID}</li>
+ * <li>{@link #SERIAL_NUMBER}</li>
+ * <li>{@link #SERVICE_CATEGORY}</li>
+ * <li>{@link #LANGUAGE_CODE}</li>
+ * <li>{@link #MESSAGE_BODY}</li>
+ * <li>{@link #DELIVERY_TIME}</li>
+ * <li>{@link #MESSAGE_READ}</li>
+ * <li>{@link #MESSAGE_FORMAT}</li>
+ * <li>{@link #MESSAGE_PRIORITY}</li>
+ * <li>{@link #ETWS_WARNING_TYPE}</li>
+ * <li>{@link #CMAS_MESSAGE_CLASS}</li>
+ * <li>{@link #CMAS_CATEGORY}</li>
+ * <li>{@link #CMAS_RESPONSE_TYPE}</li>
+ * <li>{@link #CMAS_SEVERITY}</li>
+ * <li>{@link #CMAS_URGENCY}</li>
+ * <li>{@link #CMAS_CERTAINTY}</li>
+ * </ul>
+ */
+ @RequiresPermission(Manifest.permission.READ_CELL_BROADCASTS)
+ @NonNull
+ public static final Uri MESSAGE_HISTORY_URI = Uri.parse("content://cellbroadcasts/history");
+
+ /**
* The subscription which received this cell broadcast message.
* @deprecated use {@link #SLOT_INDEX} instead.
* <P>Type: INTEGER</P>
@@ -3972,7 +4007,6 @@
/**
* The slot which received this cell broadcast message.
* <P>Type: INTEGER</P>
- * @hide
*/
public static final String SLOT_INDEX = "slot_index";
@@ -4150,14 +4184,12 @@
/**
* The timestamp in millisecond of when the device received the message.
* <P>Type: BIGINT</P>
- * @hide
*/
public static final String RECEIVED_TIME = "received_time";
/**
* Indicates that whether the message has been broadcasted to the application.
* <P>Type: BOOLEAN</P>
- * @hide
*/
public static final String MESSAGE_BROADCASTED = "message_broadcasted";
@@ -4193,7 +4225,6 @@
* "circle|0,0|100;polygon|0,0|0,1.5|1,1|1,0;circle|100.123,100|200.123"
*
* <P>Type: TEXT</P>
- * @hide
*/
public static final String GEOMETRIES = "geometries";
@@ -4205,7 +4236,6 @@
* for the alert.
*
* <P>Type: INTEGER</P>
- * @hide
*/
public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 58e15b9..b564502 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1838,6 +1838,13 @@
"support_direct_fdn_dialing_bool";
/**
+ * Int indicating the max number length for FDN
+ * @hide
+ */
+ public static final String KEY_FDN_NUMBER_LENGTH_LIMIT_INT =
+ "fdn_number_length_limit_int";
+
+ /**
* Report IMEI as device id even if it's a CDMA/LTE phone.
*
* @hide
@@ -3224,6 +3231,14 @@
public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
"disconnect_cause_play_busytone_int_array";
+ /**
+ * Flag specifying whether to prevent sending CLIR activation("*31#") and deactivation("#31#")
+ * code only without dialing number.
+ * When {@code true}, these are prevented, {@code false} otherwise.
+ */
+ public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL =
+ "prevent_clir_activation_and_deactivation_code_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3426,6 +3441,7 @@
sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
+ sDefaults.putInt(KEY_FDN_NUMBER_LENGTH_LIMIT_INT, 20);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true);
@@ -3657,6 +3673,7 @@
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
+ sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index 46eb9df..60281ad 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -17,12 +17,18 @@
package android.telephony;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.telephony.cdma.CdmaSmsCbProgramData;
+import java.util.List;
+import java.util.function.Consumer;
+
/**
* A service which exposes the cell broadcast handling module to the system.
* <p>
@@ -46,6 +52,7 @@
* </service>
* </manifest>
* }</pre>
+ *
* @hide
*/
@SystemApi
@@ -62,21 +69,38 @@
/**
* Handle a GSM cell broadcast SMS message forwarded from the system.
+ *
* @param slotIndex the index of the slot which received the message
- * @param message the SMS PDU
+ * @param message the SMS PDU
*/
public abstract void onGsmCellBroadcastSms(int slotIndex, byte[] message);
/**
* Handle a CDMA cell broadcast SMS message forwarded from the system.
- * @param slotIndex the index of the slot which received the message
- * @param bearerData the CDMA SMS bearer data
+ *
+ * @param slotIndex the index of the slot which received the message
+ * @param bearerData the CDMA SMS bearer data
* @param serviceCategory the CDMA SCPT service category
*/
public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
@CdmaSmsCbProgramData.Category int serviceCategory);
/**
+ * Handle a CDMA cell broadcast SMS message forwarded from the system.
+ *
+ * @param slotIndex the index of the slot which received the message
+ * @param smsCbProgramData the SMS CB program data of the message
+ * @param originatingAddress the originating address of the message, as a non-separated dial
+ * string
+ * @param callback a callback to run after each cell broadcast receiver has handled
+ * the SCP message. The bundle will contain a non-separated
+ * dial string as and an ArrayList of {@link CdmaSmsCbProgramResults}.
+ */
+ public abstract void onCdmaScpMessage(int slotIndex,
+ @NonNull List<CdmaSmsCbProgramData> smsCbProgramData,
+ @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback);
+
+ /**
* If overriding this method, call through to the super method for any unknown actions.
* {@inheritDoc}
*/
@@ -89,13 +113,15 @@
/**
* A wrapper around ICellBroadcastService that forwards calls to implementations of
* {@link CellBroadcastService}.
+ *
* @hide
*/
public class ICellBroadcastServiceWrapper extends ICellBroadcastService.Stub {
/**
* Handle a GSM cell broadcast SMS.
+ *
* @param slotIndex the index of the slot which received the broadcast
- * @param message the SMS message PDU
+ * @param message the SMS message PDU
*/
@Override
public void handleGsmCellBroadcastSms(int slotIndex, byte[] message) {
@@ -104,8 +130,9 @@
/**
* Handle a CDMA cell broadcast SMS.
- * @param slotIndex the index of the slot which received the broadcast
- * @param bearerData the CDMA SMS bearer data
+ *
+ * @param slotIndex the index of the slot which received the broadcast
+ * @param bearerData the CDMA SMS bearer data
* @param serviceCategory the CDMA SCPT service category
*/
@Override
@@ -114,5 +141,25 @@
CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData,
serviceCategory);
}
+
+ /**
+ * Handle a CDMA Service Category Program message.
+ *
+ * @param slotIndex the index of the slot which received the message
+ * @param smsCbProgramData the SMS CB program data of the message
+ * @param originatingAddress the originating address of the message
+ * @param callback a callback to run after each cell broadcast receiver has
+ * handled the SCP message
+ */
+ @Override
+ public void handleCdmaScpMessage(int slotIndex,
+ List<CdmaSmsCbProgramData> smsCbProgramData, String originatingAddress,
+ RemoteCallback callback) {
+ Consumer<Bundle> consumer = bundle -> {
+ callback.sendResult(bundle);
+ };
+ CellBroadcastService.this.onCdmaScpMessage(slotIndex, smsCbProgramData,
+ originatingAddress, consumer);
+ }
}
}
diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl
index bcd6cc5..11263d9 100644
--- a/telephony/java/android/telephony/ICellBroadcastService.aidl
+++ b/telephony/java/android/telephony/ICellBroadcastService.aidl
@@ -16,6 +16,9 @@
package android.telephony;
+import android.os.RemoteCallback;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
/**
* Service bound to by the system to allow custom handling of cell broadcast messages.
* <p>
@@ -29,4 +32,8 @@
/** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */
oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory);
+
+ /** @see android.telephony.CellBroadcastService#onCdmaScpMessage */
+ oneway void handleCdmaScpMessage(int slotId, in List<CdmaSmsCbProgramData> programData,
+ String originatingAddress, in RemoteCallback callback);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8425ec1..b5e91d0 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -49,7 +49,6 @@
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
-import android.os.Message;
import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
@@ -2053,13 +2052,13 @@
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static boolean isValidSlotIndex(int slotIndex) {
- return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSupportedModemCount();
+ return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getActiveModemCount();
}
/** @hide */
@UnsupportedAppUsage
public static boolean isValidPhoneId(int phoneId) {
- return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getSupportedModemCount();
+ return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount();
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2eb4809..8455e3d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1873,7 +1873,12 @@
if (telephony == null) return null;
try {
- return telephony.getMeidForSlot(slotIndex, getOpPackageName());
+ String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName());
+ if (TextUtils.isEmpty(meid)) {
+ Log.d(TAG, "getMeid: return null because MEID is not available");
+ return null;
+ }
+ return meid;
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -9507,10 +9512,12 @@
}
/**
- * Resets telephony manager settings back to factory defaults.
+ * Resets Telephony and IMS settings back to factory defaults.
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL)
public void factoryReset(int subId) {
try {
Log.d(TAG, "factoryReset: subId=" + subId);
diff --git a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.aidl b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.aidl
new file mode 100644
index 0000000..a648a0e
--- /dev/null
+++ b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.cdma;
+
+parcelable CdmaSmsCbProgramData;
+
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 2fad847..7cafa1e 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,30 +25,26 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
-import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
@@ -62,9 +58,7 @@
* @hide
*/
@SystemApi
-public class ImsMmTelManager {
-
- private static final String TAG = "ImsMmTelManager";
+public class ImsMmTelManager implements RegistrationManager {
/**
* @hide
@@ -97,94 +91,18 @@
* Callback class for receiving IMS network Registration callback events.
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
* @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead.
*/
- public static class RegistrationCallback {
-
- private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
-
- // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
- // and WWAN are more accurate constants.
- private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
- new HashMap<Integer, Integer>() {{
- // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
- // case, since it is defined.
- put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
- put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
- AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- }};
-
- private final RegistrationCallback mLocalCallback;
- private Executor mExecutor;
-
- RegistrationBinder(RegistrationCallback localCallback) {
- mLocalCallback = localCallback;
- }
-
- @Override
- public void onRegistered(int imsRadioTech) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
- mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
- }
-
- @Override
- public void onRegistering(int imsRadioTech) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
- mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
- }
-
- @Override
- public void onDeregistered(ImsReasonInfo info) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onUnregistered(info)));
- }
-
- @Override
- public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
- getAccessType(imsRadioTech), info)));
- }
-
- @Override
- public void onSubscriberAssociatedUriChanged(Uri[] uris) {
- if (mLocalCallback == null) return;
-
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() ->
- mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
- }
-
- private void setExecutor(Executor executor) {
- mExecutor = executor;
- }
-
- private static int getAccessType(int regType) {
- if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
- Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
- + regType);
- return -1;
- }
- return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
- }
- }
-
- private final RegistrationBinder mBinder = new RegistrationBinder(this);
+ // Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
+ @Deprecated
+ public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
/**
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
* @param imsTransportType the radio access technology.
*/
+ @Override
public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
}
@@ -193,6 +111,7 @@
*
* @param imsTransportType the radio access technology.
*/
+ @Override
public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
@@ -201,6 +120,7 @@
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
+ @Override
public void onUnregistered(@Nullable ImsReasonInfo info) {
}
@@ -210,33 +130,11 @@
* @param imsTransportType The transport type that has failed to handover registration to.
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
+ @Override
public void onTechnologyChangeFailed(
@AccessNetworkConstants.TransportType int imsTransportType,
@Nullable ImsReasonInfo info) {
}
-
- /**
- * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
- * it changes. Per RFC3455, an associated URI is a URI that the service provider has
- * allocated to a user for their own usage. A user's phone number is typically one of the
- * associated URIs.
- * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
- * subscription.
- * @hide
- */
- public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
- }
-
- /**@hide*/
- public final IImsRegistrationCallback getBinder() {
- return mBinder;
- }
-
- /**@hide*/
- //Only exposed as public for compatibility with deprecated ImsManager APIs.
- public void setExecutor(Executor executor) {
- mBinder.setExecutor(executor);
- }
}
/**
@@ -311,7 +209,7 @@
}
}
- private int mSubId;
+ private final int mSubId;
/**
* Create an instance of {@link ImsMmTelManager} for the subscription id specified.
@@ -356,7 +254,10 @@
* the {@link ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
+ * @deprecated Use {@link #registerImsRegistrationCallback(
+ * RegistrationManager.RegistrationCallback, Executor)} instead.
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationCallback c) throws ImsException {
@@ -366,10 +267,6 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
- if (!isImsAvailableOnDevice()) {
- throw new ImsException("IMS not available on device.",
- ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
- }
c.setExecutor(executor);
try {
getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
@@ -378,13 +275,35 @@
// Rethrow as runtime error to keep API compatible.
throw new IllegalArgumentException(e.getMessage());
} else {
- throw new RuntimeException(e.getMessage());
+ throw new ImsException(e.getMessage(), e.errorCode);
}
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ try {
+ getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
/**
* Removes an existing {@link RegistrationCallback}.
*
@@ -395,7 +314,10 @@
* @param c The {@link RegistrationCallback} to be removed.
* @see SubscriptionManager.OnSubscriptionsChangedListener
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+ * @deprecated Use {@link #unregisterImsRegistrationCallback(
+ * RegistrationManager.RegistrationCallback)}.
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
if (c == null) {
@@ -408,6 +330,69 @@
}
}
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ try {
+ getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null callback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ try {
+ getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> stateCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getRegistrationTransportType(
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (transportTypeCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null callback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ try {
+ getITelephony().getImsMmTelRegistrationTransportType(mSubId,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> transportTypeCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
/**
* Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
* availability updates for the subscription specified in
@@ -416,7 +401,7 @@
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
- * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+ * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up.
*
* When the callback is registered, it will initiate the callback c to be called with the
* current capabilities.
@@ -441,10 +426,6 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
- if (!isImsAvailableOnDevice()) {
- throw new ImsException("IMS not available on device.",
- ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
- }
c.setExecutor(executor);
try {
getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
@@ -453,7 +434,7 @@
// Rethrow as runtime error to keep API compatible.
throw new IllegalArgumentException(e.getMessage());
} else {
- throw new RuntimeException(e.getMessage());
+ throw new ImsException(e.getMessage(), e.errorCode);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -618,6 +599,46 @@
}
/**
+ * Query whether or not the requested MmTel capability is supported by the carrier on the
+ * specified network transport.
+ * <p>
+ * This is a configuration option and does not change. The only time this may change is if a
+ * new IMS configuration is loaded when there is a
+ * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription.
+ * @param capability The capability that is being queried for support on the carrier network.
+ * @param transportType The transport type of the capability to check support for.
+ * @param callback A consumer containing a Boolean result specifying whether or not the
+ * capability is supported on this carrier network for the transport specified.
+ * @param executor The executor that the callback will be called with.
+ * @throws ImsException if the subscription is no longer valid or the IMS service is not
+ * available.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @AccessNetworkConstants.TransportType int transportType,
+ @NonNull Consumer<Boolean> callback,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ try {
+ getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result == 1));
+ }
+ }, capability, transportType);
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* The user's setting for whether or not they have enabled the "Video Calling" setting.
*
* @throws IllegalArgumentException if the subscription associated with this operation is not
@@ -940,7 +961,7 @@
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- boolean isTtyOverVolteEnabled() {
+ public boolean isTtyOverVolteEnabled() {
try {
return getITelephony().isTtyOverVolteEnabled(mSubId);
} catch (ServiceSpecificException e) {
@@ -955,20 +976,39 @@
}
}
- private static boolean isImsAvailableOnDevice() {
- IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- if (pm == null) {
- // For some reason package manger is not available.. This will fail internally anyways,
- // so do not throw error and allow.
- return true;
+ /**
+ * Get the status of the MmTel Feature registered on this subscription.
+ * @param callback A callback containing an Integer describing the current state of the
+ * MmTel feature, Which will be one of the following:
+ * {@link ImsFeature#STATE_UNAVAILABLE},
+ * {@link ImsFeature#STATE_INITIALIZING},
+ * {@link ImsFeature#STATE_READY}. Will be called using the executor
+ * specified when the service state has been retrieved from the IMS service.
+ * @param executor The executor that will be used to call the callback.
+ * @throws ImsException if the IMS service associated with this subscription is not available or
+ * the IMS service is not available.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getFeatureState(@NonNull @ImsFeature.ImsState Consumer<Integer> callback,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
}
try {
- return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0);
+ getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result));
+ }
+ });
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
} catch (RemoteException e) {
- // For some reason package manger is not available.. This will fail internally anyways,
- // so do not throw error and allow.
+ e.rethrowAsRuntimeException();
}
- return true;
}
private static ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 3c343dd..25bd1ca 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -22,12 +22,14 @@
import android.annotation.RequiresPermission;
import android.content.Context;
import android.os.Binder;
+import android.telephony.AccessNetworkConstants;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Manager for interfacing with the framework RCS services, including the User Capability Exchange
@@ -36,7 +38,7 @@
* Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager.
* @hide
*/
-public class ImsRcsManager {
+public class ImsRcsManager implements RegistrationManager {
/**
* Receives RCS availability status updates from the ImsService.
@@ -136,6 +138,64 @@
mSubId = subId;
}
+ /**{@inheritDoc}*/
+ @Override
+ public void registerImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c,
+ @NonNull @CallbackExecutor Executor executor)
+ throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ throw new UnsupportedOperationException("registerImsRegistrationCallback is not"
+ + "supported.");
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void unregisterImsRegistrationCallback(
+ @NonNull RegistrationManager.RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ throw new UnsupportedOperationException("unregisterImsRegistrationCallback is not"
+ + "supported.");
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null stateCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ throw new UnsupportedOperationException("getRegistrationState is not"
+ + "supported.");
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void getRegistrationTransportType(
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ if (transportTypeCallback == null) {
+ throw new IllegalArgumentException("Must include a non-null transportTypeCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ throw new UnsupportedOperationException("getRegistrationTransportType is not"
+ + "supported.");
+ }
+
+
/**
* Registers an {@link AvailabilityCallback} with the system, which will provide RCS
* availability updates for the subscription specified.
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
new file mode 100644
index 0000000..b4c11e3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Binder;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages IMS Service registration state for associated {@link ImsFeature}s.
+ * @hide
+ */
+@SystemApi
+public interface RegistrationManager {
+
+ /**
+ * @hide
+ */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(prefix = "REGISTRATION_STATE_",
+ value = {
+ REGISTRATION_STATE_NOT_REGISTERED,
+ REGISTRATION_STATE_REGISTERING,
+ REGISTRATION_STATE_REGISTERED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsRegistrationState {}
+
+ /**
+ * The IMS service is currently not registered to the carrier network.
+ */
+ int REGISTRATION_STATE_NOT_REGISTERED = 0;
+
+ /**
+ * The IMS service is currently in the process of registering to the carrier network.
+ */
+ int REGISTRATION_STATE_REGISTERING = 1;
+
+ /**
+ * The IMS service is currently registered to the carrier network.
+ */
+ int REGISTRATION_STATE_REGISTERED = 2;
+
+
+ /**@hide*/
+ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+ // and WWAN are more accurate constants.
+ Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+ new HashMap<Integer, Integer>() {{
+ // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+ // case, since it is defined.
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }};
+
+ /**
+ * Callback class for receiving IMS network Registration callback events.
+ * @see #registerImsRegistrationCallback(RegistrationCallback, Executor)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ */
+ class RegistrationCallback {
+
+ private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+ private final RegistrationCallback mLocalCallback;
+ private Executor mExecutor;
+
+ RegistrationBinder(RegistrationCallback localCallback) {
+ mLocalCallback = localCallback;
+ }
+
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
+ }
+
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onUnregistered(info)));
+ }
+
+ @Override
+ public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+ getAccessType(imsRadioTech), info)));
+ }
+
+ @Override
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+
+ private static int getAccessType(int regType) {
+ if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
+ Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: "
+ + regType);
+ return -1;
+ }
+ return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
+ }
+ }
+
+ private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+ /**
+ * Notifies the framework when the IMS Provider is registered to the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ */
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public void onUnregistered(@Nullable ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type.
+ *
+ * @param imsTransportType The transport type that has failed to handover registration to.
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ public void onTechnologyChangeFailed(
+ @AccessNetworkConstants.TransportType int imsTransportType,
+ @Nullable ImsReasonInfo info) {
+ }
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+ * allocated to a user for their own usage. A user's phone number is typically one of the
+ * associated URIs.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ * @hide
+ */
+ public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
+ }
+
+ /**@hide*/
+ public final IImsRegistrationCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ //Only exposed as public for compatibility with deprecated ImsManager APIs.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ /**
+ * Registers a {@link RegistrationCallback} with the system. Use
+ * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current registration state.
+ *
+ * @param c The {@link RegistrationCallback} to be added.
+ * @param executor The executor the callback events should be run on.
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+ * @throws ImsException if the subscription associated with this callback is valid, but
+ * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void registerImsRegistrationCallback(@NonNull RegistrationCallback c,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException;
+
+ /**
+ * Removes an existing {@link RegistrationCallback}.
+ *
+ * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+ * etc...), this callback will automatically be removed. If this method is called for an
+ * inactive subscription, it will result in a no-op.
+ *
+ * @param c The {@link RegistrationCallback} to be removed.
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #registerImsRegistrationCallback(RegistrationCallback, Executor)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c);
+
+ /**
+ * Gets the registration state of the IMS service.
+ * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+ * registration state of the IMS service, which will be one of the
+ * following: {@link #REGISTRATION_STATE_NOT_REGISTERED},
+ * {@link #REGISTRATION_STATE_REGISTERING}, or
+ * {@link #REGISTRATION_STATE_REGISTERED}.
+ * @param executor The {@link Executor} that will be used to call the IMS registration state
+ * callback.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void getRegistrationState(@NonNull @ImsRegistrationState Consumer<Integer> stateCallback,
+ @NonNull @CallbackExecutor Executor executor);
+
+ /**
+ * Gets the Transport Type associated with the current IMS registration.
+ * @param transportTypeCallback The transport type associated with the current IMS registration,
+ * which will be one of following:
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
+ * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void getRegistrationTransportType(
+ @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback,
+ @NonNull @CallbackExecutor Executor executor);
+}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index ceb4704..8b27b6f 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -362,6 +362,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCallResult {}
+ /**
+ * If the flag is present and true, it indicates that the incoming call is for USSD.
+ * <p>
+ * This is an optional boolean flag.
+ */
+ public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+
+ /**
+ * If this flag is present and true, this call is marked as an unknown dialing call instead
+ * of an incoming call. An example of such a call is a call that is originated by sending
+ * commands (like AT commands) directly to the modem without Android involvement or dialing
+ * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
+ * certain situations.
+ * <p>
+ * This is an optional boolean flag.
+ */
+ public static final String EXTRA_IS_UNKNOWN_CALL =
+ "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+
private IImsMmTelListener mListener;
/**
@@ -410,6 +429,8 @@
/**
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+ * @param extras A bundle containing extra parameters related to the call. See
+ * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
*/
public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
@NonNull Bundle extras) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index a08e031..b455c2e 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -22,6 +22,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
@@ -72,9 +73,6 @@
// with NOT_REGISTERED in the case where the ImsService has not updated the registration state
// yet.
private static final int REGISTRATION_STATE_UNKNOWN = -1;
- private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
- private static final int REGISTRATION_STATE_REGISTERING = 1;
- private static final int REGISTRATION_STATE_REGISTERED = 2;
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@@ -128,7 +126,7 @@
* {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
+ updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
mCallbacks.broadcast((c) -> {
try {
c.onRegistered(imsRadioTech);
@@ -146,7 +144,7 @@
* {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
+ updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
mCallbacks.broadcast((c) -> {
try {
c.onRegistering(imsRadioTech);
@@ -230,7 +228,8 @@
private void updateToDisconnectedState(ImsReasonInfo info) {
synchronized (mLock) {
- updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED);
+ updateToState(REGISTRATION_TECH_NONE,
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
if (info != null) {
mLastDisconnectCause = info;
} else {
@@ -264,15 +263,15 @@
disconnectInfo = mLastDisconnectCause;
}
switch (state) {
- case REGISTRATION_STATE_NOT_REGISTERED: {
+ case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: {
c.onDeregistered(disconnectInfo);
break;
}
- case REGISTRATION_STATE_REGISTERING: {
+ case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
c.onRegistering(getConnectionType());
break;
}
- case REGISTRATION_STATE_REGISTERED: {
+ case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
c.onRegistered(getConnectionType());
break;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f79a5c6..ba3ffd4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -867,6 +867,11 @@
String getImsService(int slotId, boolean isCarrierImsService);
/**
+ * Get the MmTelFeature state attached to this subscription id.
+ */
+ void getImsMmTelFeatureState(int subId, IIntegerConsumer callback);
+
+ /**
* Set the network selection mode to automatic.
*
* @param subId the id of the subscription to update.
@@ -1795,6 +1800,16 @@
void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
/**
+ * Get the IMS service registration state for the MmTelFeature associated with this sub id.
+ */
+ void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer);
+
+ /**
+ * Get the transport type for the IMS service registration state.
+ */
+ void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer);
+
+ /**
* Adds an IMS MmTel capabilities callback for the subscription specified.
*/
void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
@@ -1815,6 +1830,12 @@
boolean isAvailable(int subId, int capability, int regTech);
/**
+ * Return whether or not the MmTel capability is supported for the requested transport type.
+ */
+ void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability,
+ int transportType);
+
+ /**
* Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
*/
boolean isAdvancedCallingSettingEnabled(int subId);
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 4d8c7d9..9d3e120 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -56,21 +56,22 @@
*/
private class InversionIContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(String callingPackage, String authority,
+ public ContentProviderResult[] applyBatch(String callingPackage,
+ @Nullable String featureId, String authority,
ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return MockContentProvider.this.applyBatch(authority, operations);
}
@Override
- public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues)
- throws RemoteException {
+ public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues[] initialValues) throws RemoteException {
return MockContentProvider.this.bulkInsert(url, initialValues);
}
@Override
- public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ String selection, String[] selectionArgs) throws RemoteException {
return MockContentProvider.this.delete(url, selection, selectionArgs);
}
@@ -80,42 +81,42 @@
}
@Override
- public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
- throws RemoteException {
+ public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues initialValues) throws RemoteException {
return MockContentProvider.this.insert(url, initialValues);
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPackage, Uri url, String mode, ICancellationSignal signal)
+ public AssetFileDescriptor openAssetFile(String callingPackage,
+ @Nullable String featureId, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPackage, Uri url, String mode, ICancellationSignal signal,
- IBinder callerToken) throws RemoteException, FileNotFoundException {
+ public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
+ throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@Override
- public Cursor query(String callingPackage, Uri url, @Nullable String[] projection,
- @Nullable Bundle queryArgs,
- @Nullable ICancellationSignal cancellationSignal)
- throws RemoteException {
+ public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.query(url, projection, queryArgs, null);
}
@Override
- public int update(String callingPackage, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int update(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues values, String selection, String[] selectionArgs)
+ throws RemoteException {
return MockContentProvider.this.update(url, values, selection, selectionArgs);
}
@Override
- public Bundle call(String callingPackage, String authority, String method, String request,
- Bundle args) throws RemoteException {
+ public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ String method, String request, Bundle args) throws RemoteException {
return MockContentProvider.this.call(authority, method, request, args);
}
@@ -130,9 +131,9 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url,
- String mimeType, Bundle opts, ICancellationSignal signal)
- throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage,
+ @Nullable String featureId, Uri url, String mimeType, Bundle opts,
+ ICancellationSignal signal) throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
@@ -142,23 +143,26 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
return MockContentProvider.this.canonicalize(uri);
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
}
@Override
- public boolean refresh(String callingPkg, Uri url, Bundle args,
- ICancellationSignal cancellationSignal) throws RemoteException {
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ Bundle args, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.refresh(url, args);
}
@Override
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ int uid, int modeFlags) {
return MockContentProvider.this.checkUriPermission(uri, uid, modeFlags);
}
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index b072d74..e512b52 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -16,14 +16,12 @@
package android.test.mock;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.EntityIterator;
import android.content.IContentProvider;
-import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
@@ -45,14 +43,15 @@
*/
public class MockIContentProvider implements IContentProvider {
@Override
- public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues) {
+ public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues[] initialValues) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("unused")
- public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ String selection, String[] selectionArgs) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -63,33 +62,33 @@
@Override
@SuppressWarnings("unused")
- public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
- throws RemoteException {
+ public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues initialValues) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPackage, Uri url, String mode, ICancellationSignal signal,
- IBinder callerToken) {
+ public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal, IBinder callerToken) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPackage, Uri uri, String mode, ICancellationSignal signal) {
+ public AssetFileDescriptor openAssetFile(String callingPackage, @Nullable String featureId,
+ Uri uri, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ContentProviderResult[] applyBatch(String callingPackage, String authority,
- ArrayList<ContentProviderOperation> operations) {
+ public ContentProviderResult[] applyBatch(String callingPackage, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Cursor query(String callingPackage, Uri url, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
+ public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -99,14 +98,14 @@
}
@Override
- public int update(String callingPackage, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int update(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues values, String selection, String[] selectionArgs) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Bundle call(String callingPackage, String authority, String method, String request,
- Bundle args) throws RemoteException {
+ public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ String method, String request, Bundle args) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -121,8 +120,9 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType,
- Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage,
+ @Nullable String featureId, Uri url, String mimeType, Bundle opts,
+ ICancellationSignal signal) throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -132,24 +132,27 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public boolean refresh(String callingPkg, Uri url, Bundle args,
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
/** {@hide} */
@Override
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
+ int modeFlags) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
}
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 81937e6..ead4a28 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -94,6 +94,8 @@
boolean sawServices = false;
for (String line : res.split("\n")) {
if (line.contains("framework.jar")) {
+ sawFramework = true; // Legacy
+ } else if (line.contains("framework-minus-apex.jar")) {
sawFramework = true;
} else if (line.contains("services.jar")) {
sawServices = true;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 19be132..023df70 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -34,6 +34,7 @@
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IOnWifiUsabilityStatsListener;
import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -142,7 +143,8 @@
boolean stopSoftAp();
- int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName);
+ int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName,
+ in SoftApConfiguration customConfig);
void stopLocalOnlyHotspot();
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.aidl b/wifi/java/android/net/wifi/SoftApConfiguration.aidl
new file mode 100644
index 0000000..1d06f45
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable SoftApConfiguration;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
new file mode 100644
index 0000000..4cc8653
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.MacAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * WiFi configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
+ *
+ * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
+ * framework how it should open a hotspot. It is not meant to describe the network as it will be
+ * seen by clients; this role is currently served by {@link WifiConfiguration} (see
+ * {@link WifiManager.LocalOnlyHotspotReservation#getWifiConfiguration()}).
+ *
+ * System apps can use this to configure a local-only hotspot using
+ * {@link WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
+ * WifiManager.LocalOnlyHotspotCallback)}.
+ *
+ * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to
+ * create a new instance.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SoftApConfiguration implements Parcelable {
+ /**
+ * SSID for the AP, or null for a framework-determined SSID.
+ */
+ private final @Nullable String mSsid;
+ /**
+ * BSSID for the AP, or null to use a framework-determined BSSID.
+ */
+ private final @Nullable MacAddress mBssid;
+ /**
+ * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK).
+ */
+ private final @Nullable String mWpa2Passphrase;
+
+ /** Private constructor for Builder and Parcelable implementation. */
+ private SoftApConfiguration(
+ @Nullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase) {
+ mSsid = ssid;
+ mBssid = bssid;
+ mWpa2Passphrase = wpa2Passphrase;
+ }
+
+ @Override
+ public boolean equals(Object otherObj) {
+ if (this == otherObj) {
+ return true;
+ }
+ if (!(otherObj instanceof SoftApConfiguration)) {
+ return false;
+ }
+ SoftApConfiguration other = (SoftApConfiguration) otherObj;
+ return Objects.equals(mSsid, other.mSsid)
+ && Objects.equals(mBssid, other.mBssid)
+ && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSsid, mBssid, mWpa2Passphrase);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mSsid);
+ dest.writeParcelable(mBssid, flags);
+ dest.writeString(mWpa2Passphrase);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() {
+ @Override
+ public SoftApConfiguration createFromParcel(Parcel in) {
+ return new SoftApConfiguration(
+ in.readString(),
+ in.readParcelable(MacAddress.class.getClassLoader()),
+ in.readString());
+ }
+
+ @Override
+ public SoftApConfiguration[] newArray(int size) {
+ return new SoftApConfiguration[size];
+ }
+ };
+
+ @Nullable
+ public String getSsid() {
+ return mSsid;
+ }
+
+ @Nullable
+ public MacAddress getBssid() {
+ return mBssid;
+ }
+
+ @Nullable
+ public String getWpa2Passphrase() {
+ return mWpa2Passphrase;
+ }
+
+ /**
+ * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
+ * Soft AP.
+ *
+ * All fields are optional. By default, SSID and BSSID are automatically chosen by the
+ * framework, and an open network is created.
+ */
+ public static final class Builder {
+ private String mSsid;
+ private MacAddress mBssid;
+ private String mWpa2Passphrase;
+
+ /**
+ * Constructs a Builder with default values (see {@link Builder}).
+ */
+ public Builder() {
+ mSsid = null;
+ mBssid = null;
+ mWpa2Passphrase = null;
+ }
+
+ /**
+ * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance.
+ */
+ public Builder(@NonNull SoftApConfiguration other) {
+ Objects.requireNonNull(other);
+
+ mSsid = other.mSsid;
+ mBssid = other.mBssid;
+ mWpa2Passphrase = other.mWpa2Passphrase;
+ }
+
+ /**
+ * Builds the {@link SoftApConfiguration}.
+ *
+ * @return A new {@link SoftApConfiguration}, as configured by previous method calls.
+ */
+ @NonNull
+ public SoftApConfiguration build() {
+ return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase);
+ }
+
+ /**
+ * Specifies an SSID for the AP.
+ *
+ * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
+ * chosen by the framework.
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the SSID is empty or not valid Unicode.
+ */
+ @NonNull
+ public Builder setSsid(@Nullable String ssid) {
+ if (ssid != null) {
+ Preconditions.checkStringNotEmpty(ssid);
+ Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid));
+ }
+ mSsid = ssid;
+ return this;
+ }
+
+ /**
+ * Specifies a BSSID for the AP.
+ *
+ * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
+ * responsible for avoiding collisions.
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC
+ * address.
+ */
+ @NonNull
+ public Builder setBssid(@Nullable MacAddress bssid) {
+ if (bssid != null) {
+ Preconditions.checkArgument(!bssid.equals(MacAddress.ALL_ZEROS_ADDRESS));
+ Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
+ }
+ mBssid = bssid;
+ return this;
+ }
+
+ /**
+ * Specifies that this AP should use WPA2-PSK with the given passphrase. When set to null
+ * and no other encryption method is configured, an open network is created.
+ *
+ * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK
+ * configuration.
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the passphrase is the empty string
+ */
+ @NonNull
+ public Builder setWpa2Passphrase(@Nullable String passphrase) {
+ if (passphrase != null) {
+ Preconditions.checkStringNotEmpty(passphrase);
+ }
+ mWpa2Passphrase = passphrase;
+ return this;
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 3dc5a62..30c24d3 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -34,6 +34,7 @@
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.BackupUtils;
import android.util.Log;
@@ -730,6 +731,14 @@
public String lastUpdateName;
/**
+ * The carrier ID identifies the operator who provides this network configuration.
+ * see {@link TelephonyManager#getSimCarrierId()}
+ * @hide
+ */
+ @SystemApi
+ public int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+ /**
* @hide
* Status of user approval for connection
*/
@@ -1850,6 +1859,7 @@
.append(" PRIO: ").append(this.priority)
.append(" HIDDEN: ").append(this.hiddenSSID)
.append(" PMF: ").append(this.requirePMF)
+ .append("CarrierId: ").append(this.carrierId)
.append('\n');
@@ -2443,6 +2453,7 @@
randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
requirePMF = source.requirePMF;
updateIdentifier = source.updateIdentifier;
+ carrierId = source.carrierId;
}
}
@@ -2517,6 +2528,7 @@
dest.writeInt(macRandomizationSetting);
dest.writeInt(osu ? 1 : 0);
dest.writeLong(randomizedMacExpirationTimeMs);
+ dest.writeInt(carrierId);
}
/** Implement the Parcelable interface {@hide} */
@@ -2593,6 +2605,7 @@
config.macRandomizationSetting = in.readInt();
config.osu = in.readInt() != 0;
config.randomizedMacExpirationTimeMs = in.readLong();
+ config.carrierId = in.readInt();
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index f8c2011..7b99a2b 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -1263,4 +1263,23 @@
public @Ocsp int getOcsp() {
return mOcsp;
}
+
+ /**
+ * If the current authentication method needs SIM card.
+ * @return true if the credential information require SIM card for current authentication
+ * method, otherwise it returns false.
+ * @hide
+ */
+ public boolean requireSimCredential() {
+ if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) {
+ return true;
+ }
+ if (mEapMethod == Eap.PEAP) {
+ if (mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA
+ || mPhase2Method == Phase2.AKA_PRIME) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8f71b0b..380ebf1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -71,6 +71,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -2747,13 +2748,6 @@
}
}
- private Executor executorForHandler(@Nullable Handler handler) {
- if (handler == null) {
- return mContext.getMainExecutor();
- }
- return new HandlerExecutor(handler);
- }
-
/**
* Request a local only hotspot that an application can use to communicate between co-located
* devices connected to the created WiFi hotspot. The network created by this method will not
@@ -2809,9 +2803,59 @@
* @param handler Handler to be used for callbacks. If the caller passes a null Handler, the
* main thread will be used.
*/
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.CHANGE_WIFI_STATE,
+ android.Manifest.permission.ACCESS_FINE_LOCATION})
public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback,
@Nullable Handler handler) {
- Executor executor = executorForHandler(handler);
+ Executor executor = handler == null ? null : new HandlerExecutor(handler);
+ startLocalOnlyHotspotInternal(null, executor, callback);
+ }
+
+ /**
+ * Starts a local-only hotspot with a specific configuration applied. See
+ * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}.
+ *
+ * Applications need either {@link android.Manifest.permission#NETWORK_SETUP_WIZARD} or
+ * {@link android.Manifest.permission#NETWORK_SETTINGS} to call this method.
+ *
+ * Since custom configuration settings may be incompatible with each other, the hotspot started
+ * through this method cannot coexist with another hotspot created through
+ * startLocalOnlyHotspot. If this is attempted, the first hotspot request wins and others
+ * receive {@link LocalOnlyHotspotCallback#ERROR_GENERIC} through
+ * {@link LocalOnlyHotspotCallback#onFailed}.
+ *
+ * @param config Custom configuration for the hotspot. See {@link SoftApConfiguration}.
+ * @param executor Executor to run callback methods on, or null to use the main thread.
+ * @param callback Callback object for updates about hotspot status, or null for no updates.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void startLocalOnlyHotspot(@NonNull SoftApConfiguration config,
+ @Nullable Executor executor,
+ @Nullable LocalOnlyHotspotCallback callback) {
+ Objects.requireNonNull(config);
+ startLocalOnlyHotspotInternal(config, executor, callback);
+ }
+
+ /**
+ * Common implementation of both configurable and non-configurable LOHS.
+ *
+ * @param config App-specified configuration, or null. When present, additional privileges are
+ * required, and the hotspot cannot be shared with other clients.
+ * @param executor Executor to run callback methods on, or null to use the main thread.
+ * @param callback Callback object for updates about hotspot status, or null for no updates.
+ */
+ private void startLocalOnlyHotspotInternal(
+ @Nullable SoftApConfiguration config,
+ @Nullable Executor executor,
+ @Nullable LocalOnlyHotspotCallback callback) {
+ if (executor == null) {
+ executor = mContext.getMainExecutor();
+ }
synchronized (mLock) {
LocalOnlyHotspotCallbackProxy proxy =
new LocalOnlyHotspotCallbackProxy(this, executor, callback);
@@ -2821,7 +2865,7 @@
throw new RemoteException("Wifi service is not running");
}
String packageName = mContext.getOpPackageName();
- int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName);
+ int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName, config);
if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
// Send message to the proxy to make sure we call back on the correct thread
proxy.onHotspotFailed(returnCode);
@@ -2902,7 +2946,8 @@
*/
public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer,
@Nullable Handler handler) {
- Executor executor = executorForHandler(handler);
+ Executor executor = handler == null ? mContext.getMainExecutor()
+ : new HandlerExecutor(handler);
synchronized (mLock) {
mLOHSObserverProxy =
new LocalOnlyHotspotObserverProxy(this, executor, observer);
@@ -3484,10 +3529,12 @@
*
* @param manager WifiManager
* @param executor Executor for delivering callbacks.
- * @param callback LocalOnlyHotspotCallback to notify the calling application.
+ * @param callback LocalOnlyHotspotCallback to notify the calling application, or null.
*/
- LocalOnlyHotspotCallbackProxy(WifiManager manager, Executor executor,
- LocalOnlyHotspotCallback callback) {
+ LocalOnlyHotspotCallbackProxy(
+ @NonNull WifiManager manager,
+ @NonNull Executor executor,
+ @Nullable LocalOnlyHotspotCallback callback) {
mWifiManager = new WeakReference<>(manager);
mExecutor = executor;
mCallback = callback;
@@ -3505,6 +3552,7 @@
}
final LocalOnlyHotspotReservation reservation =
manager.new LocalOnlyHotspotReservation(config);
+ if (mCallback == null) return;
mExecutor.execute(() -> mCallback.onStarted(reservation));
}
@@ -3514,6 +3562,7 @@
if (manager == null) return;
Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped");
+ if (mCallback == null) return;
mExecutor.execute(() -> mCallback.onStopped());
}
@@ -3524,6 +3573,7 @@
Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: "
+ reason);
+ if (mCallback == null) return;
mExecutor.execute(() -> mCallback.onFailed(reason));
}
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 9b529ce..246e96f 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -21,12 +21,15 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.net.MacAddress;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.nio.charset.CharsetEncoder;
@@ -107,6 +110,12 @@
*/
private int mPriority;
+ /**
+ * The carrier ID identifies the operator who provides this network configuration.
+ * see {@link TelephonyManager#getSimCarrierId()}
+ */
+ private int mCarrierId;
+
public Builder() {
mSsid = null;
mBssid = null;
@@ -121,6 +130,7 @@
mIsUserInteractionRequired = false;
mIsMetered = false;
mPriority = UNASSIGNED_PRIORITY;
+ mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
}
/**
@@ -258,6 +268,23 @@
}
/**
+ * Set the carrier ID of the network operator. The carrier ID associates a Suggested
+ * network with a specific carrier (and therefore SIM). The carrier ID must be provided
+ * for any network which uses the SIM-based authentication: e.g. EAP-SIM, EAP-AKA,
+ * EAP-AKA', and EAP-PEAP with SIM-based phase 2 authentication.
+ * @param carrierId see {@link TelephonyManager#getSimCarrierId()}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING)
+ public @NonNull Builder setCarrierId(int carrierId) {
+ mCarrierId = carrierId;
+ return this;
+ }
+
+ /**
* Specifies whether this represents a hidden network.
* <p>
* <li>If not set, defaults to false (i.e not a hidden network).</li>
@@ -380,6 +407,7 @@
wifiConfiguration.meteredOverride =
mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
: WifiConfiguration.METERED_OVERRIDE_NONE;
+ wifiConfiguration.carrierId = mCarrierId;
return wifiConfiguration;
}
@@ -405,6 +433,7 @@
wifiConfiguration.meteredOverride =
mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
: WifiConfiguration.METERED_OVERRIDE_NONE;
+ mPasspointConfiguration.setCarrierId(mCarrierId);
return wifiConfiguration;
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 21189a4..67993e1 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -19,7 +19,6 @@
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -134,14 +133,14 @@
* @hide
*/
@SystemApi
- @Nullable
+ @NonNull
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public List<Integer> getAvailableChannels(@WifiBand int band) {
try {
Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName());
return bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -344,7 +343,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<ScanSettings> CREATOR =
+ public static final @NonNull Creator<ScanSettings> CREATOR =
new Creator<ScanSettings>() {
public ScanSettings createFromParcel(Parcel in) {
ScanSettings settings = new ScanSettings();
@@ -492,7 +491,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<ScanData> CREATOR =
+ public static final @NonNull Creator<ScanData> CREATOR =
new Creator<ScanData>() {
public ScanData createFromParcel(Parcel in) {
int id = in.readInt();
@@ -541,7 +540,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<ParcelableScanData> CREATOR =
+ public static final @NonNull Creator<ParcelableScanData> CREATOR =
new Creator<ParcelableScanData>() {
public ParcelableScanData createFromParcel(Parcel in) {
int n = in.readInt();
@@ -589,7 +588,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<ParcelableScanResults> CREATOR =
+ public static final @NonNull Creator<ParcelableScanResults> CREATOR =
new Creator<ParcelableScanResults>() {
public ParcelableScanResults createFromParcel(Parcel in) {
int n = in.readInt();
@@ -720,7 +719,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<PnoSettings> CREATOR =
+ public static final @NonNull Creator<PnoSettings> CREATOR =
new Creator<PnoSettings>() {
public PnoSettings createFromParcel(Parcel in) {
PnoSettings settings = new PnoSettings();
@@ -1068,7 +1067,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<WifiChangeSettings> CREATOR =
+ public static final @NonNull Creator<WifiChangeSettings> CREATOR =
new Creator<WifiChangeSettings>() {
public WifiChangeSettings createFromParcel(Parcel in) {
return new WifiChangeSettings();
@@ -1179,7 +1178,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<HotlistSettings> CREATOR =
+ public static final @NonNull Creator<HotlistSettings> CREATOR =
new Creator<HotlistSettings>() {
public HotlistSettings createFromParcel(Parcel in) {
HotlistSettings settings = new HotlistSettings();
@@ -1412,7 +1411,7 @@
}
/** Implement the Parcelable interface {@hide} */
- public static final @android.annotation.NonNull Creator<OperationResult> CREATOR =
+ public static final @NonNull Creator<OperationResult> CREATOR =
new Creator<OperationResult>() {
public OperationResult createFromParcel(Parcel in) {
int reason = in.readInt();
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index e9aa076..5befb54 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -24,6 +24,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -398,6 +399,30 @@
}
/**
+ * The carrier ID identifies the operator who provides this network configuration.
+ * see {@link TelephonyManager#getSimCarrierId()}
+ */
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+ /**
+ * Set the carrier ID associated with current configuration.
+ * @param carrierId {@code mCarrierId}
+ * @hide
+ */
+ public void setCarrierId(int carrierId) {
+ this.mCarrierId = carrierId;
+ }
+
+ /**
+ * Get the carrier ID associated with current configuration.
+ * @return {@code mCarrierId}
+ * @hide
+ */
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ /**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
@@ -438,6 +463,7 @@
mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
mServiceFriendlyNames = source.mServiceFriendlyNames;
mAaaServerTrustedNames = source.mAaaServerTrustedNames;
+ mCarrierId = source.mCarrierId;
}
@Override
@@ -466,6 +492,7 @@
bundle.putSerializable("serviceFriendlyNames",
(HashMap<String, String>) mServiceFriendlyNames);
dest.writeBundle(bundle);
+ dest.writeInt(mCarrierId);
}
@Override
@@ -495,6 +522,7 @@
&& mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
&& mUsageLimitDataLimit == that.mUsageLimitDataLimit
&& mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
+ && mCarrierId == that.mCarrierId
&& (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
: mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
}
@@ -505,7 +533,7 @@
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
- mServiceFriendlyNames);
+ mServiceFriendlyNames, mCarrierId);
}
@Override
@@ -558,6 +586,7 @@
if (mServiceFriendlyNames != null) {
builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
}
+ builder.append("CarrierId:" + mCarrierId);
return builder.toString();
}
@@ -662,6 +691,7 @@
Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
"serviceFriendlyNames");
config.setServiceFriendlyNames(friendlyNamesMap);
+ config.mCarrierId = in.readInt();
return config;
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 5e6c107..4b7d205 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -32,6 +32,7 @@
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IWifiManager;
import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -293,8 +294,15 @@
throw new UnsupportedOperationException();
}
- @Override
+ /** @deprecated replaced by newer signature */
+ @Deprecated
public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName) {
+ return startLocalOnlyHotspot(callback, packageName, null);
+ }
+
+ @Override
+ public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName,
+ SoftApConfiguration customConfig) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
new file mode 100644
index 0000000..949b479
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.MacAddress;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class SoftApConfigurationTest {
+ private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(configIn, 0);
+ parcel.setDataPosition(0);
+ SoftApConfiguration configOut =
+ parcel.readParcelable(SoftApConfiguration.class.getClassLoader());
+ parcel.recycle();
+ return configOut;
+ }
+
+ @Test
+ public void testBasicSettings() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setSsid("ssid")
+ .setBssid(MacAddress.fromString("11:22:33:44:55:66"))
+ .build();
+ assertThat(original.getSsid()).isEqualTo("ssid");
+ assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
+ assertThat(original.getWpa2Passphrase()).isNull();
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+
+ @Test
+ public void testWpa2() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setWpa2Passphrase("secretsecret")
+ .build();
+ assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8d0579b..d2516a3 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -173,7 +173,7 @@
public void testCreationAndCloseOfLocalOnlyHotspotReservation() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig));
@@ -191,7 +191,7 @@
throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig));
@@ -351,7 +351,7 @@
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
verify(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
}
/**
@@ -362,7 +362,7 @@
public void testStartLocalOnlyHotspotThrowsSecurityException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new SecurityException()).when(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
}
@@ -374,7 +374,7 @@
public void testStartLocalOnlyHotspotThrowsIllegalStateException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new IllegalStateException()).when(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
}
@@ -385,7 +385,7 @@
public void testCorrectLooperIsUsedForHandler() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -404,7 +404,7 @@
when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor());
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, null);
altLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -423,7 +423,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -449,7 +449,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -474,7 +474,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -497,7 +497,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -517,7 +517,7 @@
public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -533,7 +533,7 @@
public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_TETHERING_DISALLOWED);
+ anyString(), eq(null))).thenReturn(ERROR_TETHERING_DISALLOWED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_TETHERING_DISALLOWED, callback.mFailureReason);
@@ -550,7 +550,7 @@
public void testLocalOnlyHotspotCallbackFullOnSecurityException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new SecurityException()).when(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
try {
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
} catch (SecurityException e) {
@@ -571,7 +571,7 @@
public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
//assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason);
@@ -587,7 +587,7 @@
public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
verify(mWifiService).stopLocalOnlyHotspot();
@@ -609,7 +609,7 @@
public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
verify(mWifiService).stopLocalOnlyHotspot();
@@ -628,7 +628,7 @@
public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -639,6 +639,17 @@
verify(mWifiService, never()).stopLocalOnlyHotspot();
}
+ @Test
+ public void testStartLocalOnlyHotspotForwardsCustomConfig() throws Exception {
+ SoftApConfiguration customConfig = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .build();
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ mWifiManager.startLocalOnlyHotspot(customConfig, mExecutor, callback);
+ verify(mWifiService).startLocalOnlyHotspot(
+ any(ILocalOnlyHotspotCallback.class), anyString(), eq(customConfig));
+ }
+
/**
* Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl.
*/