Merge "Verify permissions are only held by media stack."
diff --git a/api/current.txt b/api/current.txt
index 487dfd6..014ba9d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6600,7 +6600,6 @@
method @Nullable public String[] getAccountTypesWithManagementDisabled();
method @Nullable public java.util.List<android.content.ComponentName> getActiveAdmins();
method @NonNull public java.util.Set<java.lang.String> getAffiliationIds(@NonNull android.content.ComponentName);
- method public java.util.List<java.lang.String> getAlwaysOnVpnLockdownWhitelist(@NonNull android.content.ComponentName);
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
method @WorkerThread @NonNull public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
@@ -6675,7 +6674,6 @@
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(@NonNull android.content.ComponentName);
method public boolean isAffiliatedUser();
- method public boolean isAlwaysOnVpnLockdownEnabled(@NonNull android.content.ComponentName);
method public boolean isApplicationHidden(@NonNull android.content.ComponentName, String);
method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
@@ -6713,7 +6711,6 @@
method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean);
method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
- method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean, @Nullable java.util.List<java.lang.String>) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6790,7 +6787,7 @@
method public void uninstallCaCert(@Nullable android.content.ComponentName, byte[]);
method public boolean updateOverrideApn(@NonNull android.content.ComponentName, int, @NonNull android.telephony.data.ApnSetting);
method public void wipeData(int);
- method public void wipeData(int, CharSequence);
+ method public void wipeData(int, @NonNull CharSequence);
field public static final String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
field public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE";
field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
@@ -6934,6 +6931,7 @@
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
+ field public static final int WIPE_SILENTLY = 8; // 0x8
}
public abstract static class DevicePolicyManager.InstallUpdateCallback {
@@ -29842,7 +29840,7 @@
method @Deprecated public boolean disableNetwork(int);
method @Deprecated public boolean disconnect();
method @Deprecated public boolean enableNetwork(int, boolean);
- method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.DhcpInfo getDhcpInfo();
method public int getMaxNumberOfNetworkSuggestionsPerApp();
@@ -30333,26 +30331,26 @@
}
public class WifiP2pManager {
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void addServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void cancelConnect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void clearLocalServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void createGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener);
method public void removeGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener);
method public void requestDiscoveryState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
method public void requestNetworkInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener);
method public void requestP2pState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.P2pStateListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener);
method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener);
method public void setUpnpServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.UpnpServiceResponseListener);
@@ -44037,7 +44035,9 @@
field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
@@ -53492,6 +53492,7 @@
public final class TextClassificationManager {
method @NonNull public android.view.textclassifier.TextClassifier createTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext);
+ method @NonNull public android.view.textclassifier.TextClassifier getLocalTextClassifier();
method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
method public void setTextClassificationSessionFactory(@Nullable android.view.textclassifier.TextClassificationSessionFactory);
method public void setTextClassifier(@Nullable android.view.textclassifier.TextClassifier);
@@ -53569,7 +53570,7 @@
public final class TextClassifierEvent implements android.os.Parcelable {
method public int describeContents();
method @NonNull public int[] getActionIndices();
- method @Nullable public String getEntityType();
+ method @NonNull public String[] getEntityTypes();
method public int getEventCategory();
method @Nullable public android.view.textclassifier.TextClassificationContext getEventContext();
method public int getEventIndex();
@@ -53582,6 +53583,7 @@
method public int getRelativeWordEndIndex();
method public int getRelativeWordStartIndex();
method @Nullable public String getResultId();
+ method public float getScore();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3
field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4
@@ -53616,7 +53618,7 @@
ctor public TextClassifierEvent.Builder(int, int);
method @NonNull public android.view.textclassifier.TextClassifierEvent build();
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(@NonNull int...);
- method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityType(@Nullable String);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityTypes(@NonNull java.lang.String...);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(@Nullable android.view.textclassifier.TextClassificationContext);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long);
@@ -53627,6 +53629,7 @@
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int);
method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setResultId(@Nullable String);
+ method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setScore(float);
}
public final class TextLanguage implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 05e04e9..49abe28 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -883,8 +883,8 @@
public final class ClassificationsRequest implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getExtras();
- method public java.util.List<android.app.contentsuggestions.ContentSelection> getSelections();
+ method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public java.util.List<android.app.contentsuggestions.ContentSelection> getSelections();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ClassificationsRequest> CREATOR;
}
@@ -898,8 +898,8 @@
public final class ContentClassification implements android.os.Parcelable {
ctor public ContentClassification(@NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
- method public android.os.Bundle getExtras();
- method public String getId();
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public String getId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ContentClassification> CREATOR;
}
@@ -907,8 +907,8 @@
public final class ContentSelection implements android.os.Parcelable {
ctor public ContentSelection(@NonNull String, @NonNull android.os.Bundle);
method public int describeContents();
- method public android.os.Bundle getExtras();
- method public String getId();
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public String getId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ContentSelection> CREATOR;
}
@@ -930,8 +930,8 @@
public final class SelectionsRequest implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getExtras();
- method public android.graphics.Point getInterestPoint();
+ method @Nullable public android.os.Bundle getExtras();
+ method @Nullable public android.graphics.Point getInterestPoint();
method public int getTaskId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.contentsuggestions.SelectionsRequest> CREATOR;
@@ -1053,6 +1053,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
+ field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
public interface RoleManagerCallback {
@@ -1158,14 +1159,18 @@
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean getSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
field public static final int ACCESS_ALLOWED = 1; // 0x1
field public static final int ACCESS_REJECTED = 2; // 0x2
field public static final int ACCESS_UNKNOWN = 0; // 0x0
+ field public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
+ field public static final String EXTRA_SILENCE_ENABLED = "android.bluetooth.device.extra.SILENCE_ENABLED";
field public static final int METADATA_COMPANION_APP = 4; // 0x4
field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
@@ -1807,9 +1812,16 @@
}
public final class ColorDisplayManager {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public int getNightDisplayAutoMode();
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public int getTransformCapabilities();
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setAppSaturationLevel(@NonNull String, @IntRange(from=0, to=100) int);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setNightDisplayAutoMode(int);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setNightDisplayCustomEndTime(@NonNull java.time.LocalTime);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setNightDisplayCustomStartTime(@NonNull java.time.LocalTime);
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setSaturationLevel(@IntRange(from=0, to=100) int);
+ field public static final int AUTO_MODE_CUSTOM_TIME = 1; // 0x1
+ field public static final int AUTO_MODE_DISABLED = 0; // 0x0
+ field public static final int AUTO_MODE_TWILIGHT = 2; // 0x2
field public static final int CAPABILITY_HARDWARE_ACCELERATION_GLOBAL = 2; // 0x2
field public static final int CAPABILITY_HARDWARE_ACCELERATION_PER_APP = 4; // 0x4
field public static final int CAPABILITY_NONE = 0; // 0x0
@@ -1841,9 +1853,15 @@
public final class HdmiControlManager {
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @Nullable public android.hardware.hdmi.HdmiClient getClient(int);
+ method @Nullable public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevicesList();
+ method public int getPhysicalAddress();
method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
+ method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
+ method public boolean isRemoteDeviceConnected(android.hardware.hdmi.HdmiDeviceInfo);
+ method public void powerOffRemoteDevice(android.hardware.hdmi.HdmiDeviceInfo);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
+ method public void requestRemoteDeviceToBecomeActiveSource(android.hardware.hdmi.HdmiDeviceInfo);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
field public static final int AVR_VOLUME_MUTED = 101; // 0x65
@@ -1933,6 +1951,9 @@
field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
}
+ @IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface HdmiControlManager.ControlCallbackResult {
+ }
+
public static interface HdmiControlManager.HotplugEventListener {
method public void onReceived(android.hardware.hdmi.HdmiHotplugEvent);
}
@@ -2059,6 +2080,16 @@
public abstract static class HdmiRecordSources.RecordSource {
}
+ public class HdmiSwitchClient extends android.hardware.hdmi.HdmiClient {
+ method public int getDeviceType();
+ method public void selectPort(int, @NonNull android.hardware.hdmi.HdmiSwitchClient.OnSelectListener);
+ method public void selectPort(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiSwitchClient.OnSelectListener);
+ }
+
+ public static interface HdmiSwitchClient.OnSelectListener {
+ method public void onSelect(@android.hardware.hdmi.HdmiControlManager.ControlCallbackResult int);
+ }
+
public class HdmiTimerRecordSources {
method public static boolean checkTimerRecordSource(int, byte[]);
method public static android.hardware.hdmi.HdmiTimerRecordSources.Duration durationOf(int, int);
@@ -4647,7 +4678,7 @@
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
- method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
method public boolean isDeviceToDeviceRttSupported();
@@ -5655,6 +5686,11 @@
method public void onPropertyChanged(String, String, String);
}
+ public static interface DeviceConfig.Storage {
+ field public static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
+ field public static final String NAMESPACE = "storage";
+ }
+
public static interface DeviceConfig.Telephony {
field public static final String NAMESPACE = "telephony";
field public static final String PROPERTY_ENABLE_RAMPING_RINGER = "enable_ramping_ringer";
@@ -7980,10 +8016,10 @@
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
- field public static final String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+ field public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4; // 0x4
field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
@@ -8676,12 +8712,20 @@
public class ProvisioningManager {
method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getProvisioningIntValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getProvisioningStringValue(int);
+ method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getProvisioningIntValue(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getProvisioningStringValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setProvisioningIntValue(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setProvisioningStringValue(int, String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+ field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
+ field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
+ field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
+ field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+ field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
+ field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
}
public static class ProvisioningManager.Callback {
diff --git a/api/test-current.txt b/api/test-current.txt
index f1795c7..0f2ba12 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -15,6 +15,10 @@
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
}
+ public static final class R.array {
+ field public static final int config_defaultRoleHolders = 17235974; // 0x1070006
+ }
+
}
package android.animation {
@@ -333,6 +337,7 @@
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
+ field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
}
public interface RoleManagerCallback {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3defdc5..062ba65 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -102,7 +102,17 @@
String op = nextArg();
Slog.v(TAG, "Running " + op + " for user:" + userId);
- if (!isBmgrActive(userId)) {
+ if (mBmgr == null) {
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ return;
+ }
+
+ if ("activate".equals(op)) {
+ doActivateService(userId);
+ return;
+ }
+
+ if (!isBackupActive(userId)) {
return;
}
@@ -175,12 +185,7 @@
showUsage();
}
- boolean isBmgrActive(@UserIdInt int userId) {
- if (mBmgr == null) {
- System.err.println(BMGR_NOT_RUNNING_ERR);
- return false;
- }
-
+ boolean isBackupActive(@UserIdInt int userId) {
try {
if (!mBmgr.isBackupServiceActive(userId)) {
System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -845,6 +850,27 @@
}
}
+ private void doActivateService(int userId) {
+ String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
+ try {
+ boolean activate = Boolean.parseBoolean(arg);
+ mBmgr.setBackupServiceActive(userId, activate);
+ System.out.println(
+ "Backup service now "
+ + (activate ? "activated" : "deactivated")
+ + " for user "
+ + userId);
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
@@ -880,6 +906,7 @@
System.err.println(" bmgr backupnow [--monitor|--monitor-verbose] --all|PACKAGE...");
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
+ System.err.println(" bmgr activate BOOL");
System.err.println("");
System.err.println("The '--user' option specifies the user on which the operation is run.");
System.err.println("It must be the first argument before the operation.");
@@ -946,6 +973,11 @@
System.err.println("");
System.err.println("The 'init' command initializes the given transports, wiping all data");
System.err.println("from their backing data stores.");
+ System.err.println("");
+ System.err.println("The 'activate' command activates or deactivates the backup service.");
+ System.err.println("If the argument is 'true' it will be activated, otherwise it will be");
+ System.err.println("deactivated. When deactivated, the service will not be running and no");
+ System.err.println("operations can be performed until activation.");
}
private static class BackupMonitor extends IBackupManagerMonitor.Stub {
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index c455ac0..0c581f3 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -39,6 +39,7 @@
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::UidHasWriteAccessToPath;
bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
std::string target_apk_path;
@@ -66,6 +67,13 @@
return false;
}
+ const uid_t uid = getuid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ out_error << "error: uid " << uid << " does not have write access to " << idmap_path
+ << std::endl;
+ return false;
+ }
+
PolicyBitmask fulfilled_policies = 0;
if (auto result = PoliciesToBitmask(policies, out_error)) {
fulfilled_policies |= *result;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index a3c7527..f30ce9b 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -27,6 +27,7 @@
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
+#include "binder/IPCThreadState.h"
#include "utils/String8.h"
#include "utils/Trace.h"
@@ -38,18 +39,19 @@
#include "idmap2d/Idmap2Service.h"
+using android::IPCThreadState;
using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
using android::idmap2::PolicyBitmask;
using android::idmap2::Result;
+using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::UidHasWriteAccessToPath;
namespace {
-constexpr const char* kIdmapCacheDir = "/data/resource-cache";
-
Status ok() {
return Status::ok();
}
@@ -77,7 +79,13 @@
Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
assert(_aidl_return);
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ *_aidl_return = false;
+ return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
+ idmap_path.c_str(), uid));
+ }
if (unlink(idmap_path.c_str()) != 0) {
*_aidl_return = false;
return error("failed to unlink " + idmap_path + ": " + strerror(errno));
@@ -118,6 +126,13 @@
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss",
+ idmap_path.c_str(), uid));
+ }
+
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return error("failed to load apk " + target_apk_path);
@@ -137,7 +152,6 @@
}
umask(kIdmapFilePermissionMask);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 5c41c49..3f03236 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,6 +17,8 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+#include <sys/types.h>
+
#include <functional>
#include <memory>
#include <string>
@@ -24,6 +26,7 @@
namespace android::idmap2::utils {
+constexpr const char* kIdmapCacheDir = "/data/resource-cache";
constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
@@ -35,6 +38,8 @@
std::unique_ptr<std::string> ReadFile(const std::string& path);
+bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
+
} // namespace android::idmap2::utils
#endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 0255727..a9b68cd 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -19,12 +19,20 @@
#include <unistd.h>
#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <cstring>
#include <fstream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "private/android_filesystem_config.h"
+
#include "idmap2/FileUtils.h"
namespace android::idmap2::utils {
@@ -77,4 +85,26 @@
return r == 0 ? std::move(str) : nullptr;
}
+#ifdef __ANDROID__
+bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
+ // resolve symlinks and relative paths; the directories must exist
+ std::string canonical_path;
+ if (!base::Realpath(base::Dirname(path), &canonical_path)) {
+ return false;
+ }
+
+ const std::string cache_subdir = base::StringPrintf("%s/", kIdmapCacheDir);
+ if (canonical_path == kIdmapCacheDir ||
+ canonical_path.compare(0, cache_subdir.size(), cache_subdir) == 0) {
+ // limit access to /data/resource-cache to root and system
+ return uid == AID_ROOT || uid == AID_SYSTEM;
+ }
+ return true;
+}
+#else
+bool UidHasWriteAccessToPath(uid_t uid ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) {
+ return true;
+}
+#endif
+
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index d9d9a7f..45f84fe 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -22,6 +22,8 @@
#include "gtest/gtest.h"
#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "private/android_filesystem_config.h"
#include "idmap2/FileUtils.h"
@@ -71,4 +73,25 @@
close(pipefd[0]);
}
+#ifdef __ANDROID__
+TEST(FileUtilsTests, UidHasWriteAccessToPath) {
+ constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
+ const std::string cache_path(base::StringPrintf("%s/test@idmap", kIdmapCacheDir));
+ const std::string sneaky_cache_path(base::StringPrintf("/data/../%s/test@idmap", kIdmapCacheDir));
+
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, tmp_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, cache_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, sneaky_cache_path));
+
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SYSTEM, tmp_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SYSTEM, cache_path));
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SYSTEM, sneaky_cache_path));
+
+ constexpr const uid_t AID_SOME_APP = AID_SYSTEM + 1;
+ ASSERT_TRUE(UidHasWriteAccessToPath(AID_SOME_APP, tmp_path));
+ ASSERT_FALSE(UidHasWriteAccessToPath(AID_SOME_APP, cache_path));
+ ASSERT_FALSE(UidHasWriteAccessToPath(AID_SOME_APP, sneaky_cache_path));
+}
+#endif
+
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 4334fa6..c550eaf 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -38,6 +38,7 @@
#include "gtest/gtest.h"
#include "androidfw/PosixUtils.h"
+#include "private/android_filesystem_config.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
@@ -69,9 +70,23 @@
ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
} while (0)
+#ifdef __ANDROID__
+#define SKIP_TEST_IF_CANT_EXEC_IDMAP2 \
+ do { \
+ const uid_t uid = getuid(); \
+ if (uid != AID_ROOT && uid != AID_SYSTEM) { \
+ GTEST_SKIP(); \
+ } \
+ } while (0)
+#else
+#define SKIP_TEST_IF_CANT_EXEC_IDMAP2
+#endif
+
} // namespace
TEST_F(Idmap2BinaryTests, Create) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
// clang-format off
auto result = ExecuteBinary({"idmap2",
"create",
@@ -97,6 +112,8 @@
}
TEST_F(Idmap2BinaryTests, Dump) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
// clang-format off
auto result = ExecuteBinary({"idmap2",
"create",
@@ -144,6 +161,8 @@
}
TEST_F(Idmap2BinaryTests, Scan) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
const std::string idmap_static_1_path =
@@ -236,6 +255,8 @@
}
TEST_F(Idmap2BinaryTests, Lookup) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
// clang-format off
auto result = ExecuteBinary({"idmap2",
"create",
@@ -285,6 +306,8 @@
}
TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
+ SKIP_TEST_IF_CANT_EXEC_IDMAP2;
+
const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
// missing mandatory options
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8fb01b4..d6b4737f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2492,24 +2492,32 @@
/*
* Logs when a binary push state changes.
- * Logged in Play store
+ * Logged by the installer via public api.
*/
message BinaryPushStateChanged {
- // Binary package name.
- optional string binary_name = 1;
- // Version code.
- optional int64 version = 2;
- // State
+ // Name of the train.
+ optional string train_name = 1;
+ // Version code for a "train" of packages that need to be installed atomically
+ optional int64 train_version_code = 2;
+ // After installation of this package, device requires a restart.
+ optional bool requires_staging = 3;
+ // Rollback should be enabled for this install.
+ optional bool rollback_enabled = 4;
+ // Requires low latency monitoring if possible.
+ optional bool requires_low_latency_monitor = 5;
+
enum State {
- STATE_UNKNOWN = 0;
- DOWNLOAD_START = 1;
- DOWNLOAD_DONE = 2;
- INSTALL_START = 3;
- INSTALL_DONE = 4;
- REBOOT_START = 5;
- REBOOT_DONE = 6;
+ UNKNOWN = 0;
+ INSTALL_REQUESTED = 1;
+ INSTALL_STARTED = 2;
+ INSTALL_STAGED_NOT_READY = 3;
+ INSTALL_STAGED_READY = 4;
+ INSTALL_SUCCESS = 5;
+ INSTALL_FAILURE = 6;
+ INSTALL_CANCELLED = 7;
+ INSTALLER_ROLLBACK_REQUESTED = 8;
}
- optional State state = 3;
+ optional State state = 6;
}
/** Represents USB port overheat event. */
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 4d3711a..4739867 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager.StackInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
@@ -119,6 +120,7 @@
/** Callback that notifies when the container is ready or destroyed. */
public abstract static class StateCallback {
+
/**
* Called when the container is ready for launching activities. Calling
* {@link #startActivity(Intent)} prior to this callback will result in an
@@ -127,6 +129,7 @@
* @see #startActivity(Intent)
*/
public abstract void onActivityViewReady(ActivityView view);
+
/**
* Called when the container can no longer launch activities. Calling
* {@link #startActivity(Intent)} after this callback will result in an
@@ -135,11 +138,24 @@
* @see #startActivity(Intent)
*/
public abstract void onActivityViewDestroyed(ActivityView view);
+
+ /**
+ * Called when a task is created inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskCreated(int taskId, ComponentName componentName) { }
+
/**
* Called when a task is moved to the front of the stack inside the container.
* This is a filtered version of {@link TaskStackListener}
*/
public void onTaskMovedToFront(ActivityManager.StackInfo stackInfo) { }
+
+ /**
+ * Called when a task is about to be removed from the stack inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskRemovalStarted(int taskId) { }
}
/**
@@ -508,14 +524,45 @@
@Override
public void onTaskMovedToFront(int taskId) throws RemoteException {
- if (mActivityViewCallback != null) {
- StackInfo stackInfo = getTopMostStackInfo();
- // if StackInfo was null or unrelated to the "move to front" then there's no use
- // notifying the callback
- if (stackInfo != null
- && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
- mActivityViewCallback.onTaskMovedToFront(stackInfo);
- }
+ if (mActivityViewCallback == null) {
+ return;
+ }
+
+ StackInfo stackInfo = getTopMostStackInfo();
+ // if StackInfo was null or unrelated to the "move to front" then there's no use
+ // notifying the callback
+ if (stackInfo != null
+ && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+ mActivityViewCallback.onTaskMovedToFront(stackInfo);
+ }
+ }
+
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
+ if (mActivityViewCallback == null) {
+ return;
+ }
+
+ StackInfo stackInfo = getTopMostStackInfo();
+ // if StackInfo was null or unrelated to the task creation then there's no use
+ // notifying the callback
+ if (stackInfo != null
+ && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+ mActivityViewCallback.onTaskCreated(taskId, componentName);
+ }
+ }
+
+ @Override
+ public void onTaskRemovalStarted(int taskId) throws RemoteException {
+ if (mActivityViewCallback == null) {
+ return;
+ }
+ StackInfo stackInfo = getTopMostStackInfo();
+ // if StackInfo was null or task is on a different display then there's no use
+ // notifying the callback
+ if (stackInfo != null
+ && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+ mActivityViewCallback.onTaskRemovalStarted(taskId);
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ab2430c..364d3c9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -39,6 +39,7 @@
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -1712,6 +1713,12 @@
/** @hide */
public static final String KEY_HISTORICAL_OPS = "historical_ops";
+ /** System properties for debug logging of noteOp call sites */
+ private static final String DEBUG_LOGGING_ENABLE_PROP = "appops.logging_enabled";
+ private static final String DEBUG_LOGGING_PACKAGES_PROP = "appops.logging_packages";
+ private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
+ private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
+
/**
* Retrieve the op switch that controls the given operation.
* @hide
@@ -4469,6 +4476,7 @@
*/
@UnsupportedAppUsage
public int noteOpNoThrow(int op, int uid, String packageName) {
+ logNoteOpIfNeeded(op, packageName);
try {
return mService.noteOperation(op, uid, packageName);
} catch (RemoteException e) {
@@ -4834,4 +4842,45 @@
return AppOpsManager.MODE_DEFAULT;
}
+
+ private static void logNoteOpIfNeeded(int op, String callingPackage) {
+ // Check if debug logging propety is enabled.
+ if (!SystemProperties.getBoolean(DEBUG_LOGGING_ENABLE_PROP, false)) {
+ return;
+ }
+ // Check if this package should be logged.
+ String packages = SystemProperties.get(DEBUG_LOGGING_PACKAGES_PROP, "");
+ if (!"".equals(packages) && callingPackage != null) {
+ boolean found = false;
+ for (String pkg : packages.split(",")) {
+ if (callingPackage.equals(pkg)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+ }
+ String opStr = opToName(op);
+ // Check if this app op should be logged
+ String logOps = SystemProperties.get(DEBUG_LOGGING_OPS_PROP, "");
+ if (!"".equals(logOps)) {
+ boolean found = false;
+ for (String logOp : logOps.split(",")) {
+ if (opStr.equals(logOp)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+ }
+
+ // Log a stack trace
+ Exception here = new Exception("HERE!");
+ android.util.Log.i(DEBUG_LOGGING_TAG, "Note operation package= " + callingPackage
+ + " op= " + opStr, here);
+ }
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index cc6c999..db6ad3d 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -434,6 +434,11 @@
void registerRemoteAnimationForNextActivityStart(in String packageName,
in RemoteAnimationAdapter adapter);
+ /**
+ * Registers remote animations for a display.
+ */
+ void registerRemoteAnimationsForDisplay(int displayId, in RemoteAnimationDefinition definition);
+
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
void alwaysShowUnsupportedCompileSdkWarning(in ComponentName activity);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b8d748d..b405d0c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5257,7 +5257,11 @@
* @hide
*/
public RemoteViews makeAmbientNotification() {
- return createHeadsUpContentView(false /* increasedHeight */);
+ RemoteViews headsUpContentView = createHeadsUpContentView(false /* increasedHeight */);
+ if (headsUpContentView != null) {
+ return headsUpContentView;
+ }
+ return createContentView();
}
private void hideLine1Text(RemoteViews result) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ee91fa1..2514eee 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3980,6 +3980,10 @@
*/
public static final int WIPE_EUICC = 0x0004;
+ /**
+ * Flag for {@link #wipeData(int)}: won't show reason for wiping to the user.
+ */
+ public static final int WIPE_SILENTLY = 0x0008;
/**
* Ask that all user data be wiped. If called as a secondary user, the user will be removed and
@@ -3991,9 +3995,10 @@
* be able to call this method; if it has not, a security exception will be thrown.
*
* @param flags Bit mask of additional options: currently supported flags are
- * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
+ * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA},
+ * {@link #WIPE_EUICC} and {@link #WIPE_SILENTLY}.
* @throws SecurityException if the calling application does not own an active administrator
- * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
+ * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
*/
public void wipeData(int flags) {
throwIfParentInstance("wipeData");
@@ -4013,16 +4018,21 @@
* be able to call this method; if it has not, a security exception will be thrown.
*
* @param flags Bit mask of additional options: currently supported flags are
- * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
+ * {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and
+ * {@link #WIPE_EUICC}.
* @param reason a string that contains the reason for wiping data, which can be
- * presented to the user. If the string is null or empty, user won't be notified.
+ * presented to the user.
* @throws SecurityException if the calling application does not own an active administrator
- * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
- * @throws IllegalArgumentException if the input reason string is null or empty.
+ * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
+ * @throws IllegalArgumentException if the input reason string is null or empty, or if
+ * {@link #WIPE_SILENTLY} is set.
*/
- public void wipeData(int flags, CharSequence reason) {
+ public void wipeData(int flags, @NonNull CharSequence reason) {
throwIfParentInstance("wipeData");
- wipeDataInternal(flags, reason != null ? reason.toString() : null);
+ Preconditions.checkNotNull(reason, "reason string is null");
+ Preconditions.checkStringNotEmpty(reason, "reason string is empty");
+ Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set");
+ wipeDataInternal(flags, reason.toString());
}
/**
@@ -4033,7 +4043,7 @@
* @see #wipeData(int, CharSequence)
* @hide
*/
- private void wipeDataInternal(int flags, String wipeReasonForUser) {
+ private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
if (mService != null) {
try {
mService.wipeDataWithReason(flags, wipeReasonForUser);
@@ -5049,16 +5059,11 @@
}
/**
- * Service-specific error code used in implementation of {@code setAlwaysOnVpnPackage} methods.
- * @hide
- */
- public static final int ERROR_VPN_PACKAGE_NOT_FOUND = 1;
-
- /**
* Called by a device or profile owner to configure an always-on VPN connection through a
* specific application for the current user. This connection is automatically granted and
* persisted after a reboot.
- * <p> To support the always-on feature, an app must
+ * <p>
+ * To support the always-on feature, an app must
* <ul>
* <li>declare a {@link android.net.VpnService} in its manifest, guarded by
* {@link android.Manifest.permission#BIND_VPN_SERVICE};</li>
@@ -5067,13 +5072,12 @@
* {@link android.net.VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}.</li>
* </ul>
* The call will fail if called with the package name of an unsupported VPN app.
- * <p> Enabling lockdown via {@code lockdownEnabled} argument carries the risk that any failure
- * of the VPN provider could break networking for all apps.
*
* @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to
* remove an existing always-on VPN configuration.
* @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
- * {@code false} otherwise. This has no effect when clearing.
+ * {@code false} otherwise. This carries the risk that any failure of the VPN provider
+ * could break networking for all apps. This has no effect when clearing.
* @throws SecurityException if {@code admin} is not a device or a profile owner.
* @throws NameNotFoundException if {@code vpnPackage} is not installed.
* @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
@@ -5082,46 +5086,11 @@
public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
boolean lockdownEnabled)
throws NameNotFoundException, UnsupportedOperationException {
- setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled, Collections.emptyList());
- }
-
- /**
- * A version of {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} that allows the
- * admin to specify a set of apps that should be able to access the network directly when VPN
- * is not connected. When VPN connects these apps switch over to VPN if allowed to use that VPN.
- * System apps can always bypass VPN.
- * <p> Note that the system doesn't update the whitelist when packages are installed or
- * uninstalled, the admin app must call this method to keep the list up to date.
- *
- * @param vpnPackage package name for an installed VPN app on the device, or {@code null}
- * to remove an existing always-on VPN configuration
- * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
- * {@code false} otherwise. This has no effect when clearing.
- * @param lockdownWhitelist Packages that will be able to access the network directly when VPN
- * is in lockdown mode but not connected. Has no effect when clearing.
- * @throws SecurityException if {@code admin} is not a device or a profile
- * owner.
- * @throws NameNotFoundException if {@code vpnPackage} or one of
- * {@code lockdownWhitelist} is not installed.
- * @throws UnsupportedOperationException if {@code vpnPackage} exists but does
- * not support being set as always-on, or if always-on VPN is not
- * available.
- */
- public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
- boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist)
- throws NameNotFoundException, UnsupportedOperationException {
throwIfParentInstance("setAlwaysOnVpnPackage");
if (mService != null) {
try {
- mService.setAlwaysOnVpnPackage(
- admin, vpnPackage, lockdownEnabled, lockdownWhitelist);
- } catch (ServiceSpecificException e) {
- switch (e.errorCode) {
- case ERROR_VPN_PACKAGE_NOT_FOUND:
- throw new NameNotFoundException(e.getMessage());
- default:
- throw new RuntimeException(
- "Unknown error setting always-on VPN: " + e.errorCode);
+ if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled)) {
+ throw new NameNotFoundException(vpnPackage);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -5130,51 +5099,6 @@
}
/**
- * Called by device or profile owner to query whether current always-on VPN is configured in
- * lockdown mode. Returns {@code false} when no always-on configuration is set.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- *
- * @throws SecurityException if {@code admin} is not a device or a profile owner.
- *
- * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean)
- */
- public boolean isAlwaysOnVpnLockdownEnabled(@NonNull ComponentName admin) {
- throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
- if (mService != null) {
- try {
- return mService.isAlwaysOnVpnLockdownEnabled(admin);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return false;
- }
-
- /**
- * Called by device or profile owner to query the list of packages that are allowed to access
- * the network directly when always-on VPN is in lockdown mode but not connected. Returns
- * {@code null} when always-on VPN is not active or not in lockdown mode.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- *
- * @throws SecurityException if {@code admin} is not a device or a profile owner.
- *
- * @see #setAlwaysOnVpnPackage(ComponentName, String, boolean, List)
- */
- public List<String> getAlwaysOnVpnLockdownWhitelist(@NonNull ComponentName admin) {
- throwIfParentInstance("getAlwaysOnVpnLockdownWhitelist");
- if (mService != null) {
- try {
- return mService.getAlwaysOnVpnLockdownWhitelist(admin);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return null;
- }
-
- /**
* Called by a device or profile owner to read the name of the package administering an
* always-on VPN connection for the current user. If there is no such package, or the always-on
* VPN is provided by the system instead of by an application, {@code null} will be returned.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5790fda..1751a91c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -187,10 +187,8 @@
void setCertInstallerPackage(in ComponentName who, String installerPackage);
String getCertInstallerPackage(in ComponentName who);
- boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownWhitelist);
+ boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown);
String getAlwaysOnVpnPackage(in ComponentName who);
- boolean isAlwaysOnVpnLockdownEnabled(in ComponentName who);
- List<String> getAlwaysOnVpnLockdownWhitelist(in ComponentName who);
void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
diff --git a/core/java/android/app/contentsuggestions/ClassificationsRequest.java b/core/java/android/app/contentsuggestions/ClassificationsRequest.java
index 3f71518..9bb39e5 100644
--- a/core/java/android/app/contentsuggestions/ClassificationsRequest.java
+++ b/core/java/android/app/contentsuggestions/ClassificationsRequest.java
@@ -26,6 +26,11 @@
import java.util.List;
/**
+ * Request object used when asking {@link ContentSuggestionsManager} to classify content selections.
+ *
+ * <p>The request contains a list of {@link ContentSelection} objects to be classified along with
+ * implementation specific extras.
+ *
* @hide
*/
@SystemApi
@@ -44,14 +49,14 @@
/**
* Return request selections.
*/
- public List<ContentSelection> getSelections() {
+ public @NonNull List<ContentSelection> getSelections() {
return mSelections;
}
/**
- * Return the request extras.
+ * Return the request extras or {@code null} if there are none.
*/
- public Bundle getExtras() {
+ public @Nullable Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/contentsuggestions/ContentClassification.java b/core/java/android/app/contentsuggestions/ContentClassification.java
index f18520f..2a00b40 100644
--- a/core/java/android/app/contentsuggestions/ContentClassification.java
+++ b/core/java/android/app/contentsuggestions/ContentClassification.java
@@ -23,6 +23,9 @@
import android.os.Parcelable;
/**
+ * Represents the classification of a content suggestion. The result of a
+ * {@link ClassificationsRequest} to {@link ContentSuggestionsManager}.
+ *
* @hide
*/
@SystemApi
@@ -32,6 +35,12 @@
@NonNull
private final Bundle mExtras;
+ /**
+ * Default constructor.
+ *
+ * @param classificationId implementation specific id for the selection / classification.
+ * @param extras containing the classification data.
+ */
public ContentClassification(@NonNull String classificationId, @NonNull Bundle extras) {
mClassificationId = classificationId;
mExtras = extras;
@@ -40,14 +49,14 @@
/**
* Return the classification id.
*/
- public String getId() {
+ public @NonNull String getId() {
return mClassificationId;
}
/**
* Return the classification extras.
*/
- public Bundle getExtras() {
+ public @NonNull Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/contentsuggestions/ContentSelection.java b/core/java/android/app/contentsuggestions/ContentSelection.java
index a8917f7..16b4f3f 100644
--- a/core/java/android/app/contentsuggestions/ContentSelection.java
+++ b/core/java/android/app/contentsuggestions/ContentSelection.java
@@ -23,6 +23,9 @@
import android.os.Parcelable;
/**
+ * Represents a suggested selection within a set of on screen content. The result of a
+ * {@link SelectionsRequest} to {@link ContentSuggestionsManager}.
+ *
* @hide
*/
@SystemApi
@@ -32,6 +35,12 @@
@NonNull
private final Bundle mExtras;
+ /**
+ * Default constructor.
+ *
+ * @param selectionId implementation specific id for the selection.
+ * @param extras containing the data that represents the selection.
+ */
public ContentSelection(@NonNull String selectionId, @NonNull Bundle extras) {
mSelectionId = selectionId;
mExtras = extras;
@@ -40,14 +49,14 @@
/**
* Return the selection id.
*/
- public String getId() {
+ public @NonNull String getId() {
return mSelectionId;
}
/**
* Return the selection extras.
*/
- public Bundle getExtras() {
+ public @NonNull Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/contentsuggestions/SelectionsRequest.java b/core/java/android/app/contentsuggestions/SelectionsRequest.java
index 16f3e6b..e3c8bc5 100644
--- a/core/java/android/app/contentsuggestions/SelectionsRequest.java
+++ b/core/java/android/app/contentsuggestions/SelectionsRequest.java
@@ -25,6 +25,12 @@
import android.os.Parcelable;
/**
+ * The request object used to request content selections from {@link ContentSuggestionsManager}.
+ *
+ * <p>Selections are requested for a given taskId as specified by
+ * {@link android.app.ActivityManager} and optionally take an interest point that specifies the
+ * point on the screen that should be considered as the most important.
+ *
* @hide
*/
@SystemApi
@@ -49,16 +55,17 @@
}
/**
- * Return the request point of interest.
+ * Return the request point of interest or {@code null} if there is no point of interest for
+ * this request.
*/
- public Point getInterestPoint() {
+ public @Nullable Point getInterestPoint() {
return mInterestPoint;
}
/**
- * Return the request extras.
+ * Return the request extras or {@code null} if there aren't any.
*/
- public Bundle getExtras() {
+ public @Nullable Bundle getExtras() {
return mExtras;
}
@@ -99,6 +106,11 @@
private Point mInterestPoint;
private Bundle mExtras;
+ /**
+ * Default constructor.
+ *
+ * @param taskId of the type used by {@link android.app.ActivityManager}
+ */
public Builder(int taskId) {
mTaskId = taskId;
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index a6abe0b..ddd5313 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -172,6 +172,15 @@
public static final String ROLE_CALL_COMPANION_APP = "android.app.role.CALL_COMPANION_APP";
/**
+ * The name of the assistant app role.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
+
+ /**
* The action used to request user approval of a role for an application.
*
* @hide
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 17cf702..4d8dc35 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -532,6 +532,28 @@
"android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
/**
+ * Intent to broadcast silence mode changed.
+ * Alway contains the extra field {@link #EXTRA_DEVICE}
+ * Alway contains the extra field {@link #EXTRA_SILENCE_ENABLED}
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_SILENCE_MODE_CHANGED =
+ "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
+
+ /**
+ * Used as an extra field in {@link #ACTION_SILENCE_MODE_CHANGED} intent,
+ * contains whether device is in silence mode as boolean.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SILENCE_ENABLED =
+ "android.bluetooth.device.extra.SILENCE_ENABLED";
+
+ /**
* Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent.
*
* @hide
@@ -1592,6 +1614,70 @@
}
/**
+ * Set the Bluetooth device silence mode.
+ *
+ * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice}
+ * is an active device (for A2DP or HFP), the active device for that profile
+ * will be set to null.
+ * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP
+ * active device is null, the {@link BluetoothDevice} will be set as the
+ * active device for that profile.
+ * If the {@link BluetoothDevice} is disconnected, it exits silence mode.
+ * If the {@link BluetoothDevice} is set as the active device for A2DP or
+ * HFP, while silence mode is enabled, then the device will exit silence mode.
+ * If the {@link BluetoothDevice} is in silence mode, AVRCP position change
+ * event and HFP AG indicators will be disabled.
+ * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
+ * enter silence mode.
+ *
+ * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+ *
+ * @param silence true to enter silence mode, false to exit
+ * @return true on success, false on error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setSilenceMode(boolean silence) {
+ final IBluetooth service = sService;
+ if (service == null) {
+ return false;
+ }
+ try {
+ if (getSilenceMode() == silence) {
+ return true;
+ }
+ return service.setSilenceMode(this, silence);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setSilenceMode fail", e);
+ return false;
+ }
+ }
+
+ /**
+ * Get the device silence mode status
+ *
+ * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+ *
+ * @return true on device in silence mode, otherwise false.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean getSilenceMode() {
+ final IBluetooth service = sService;
+ if (service == null) {
+ return false;
+ }
+ try {
+ return service.getSilenceMode(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getSilenceMode fail", e);
+ return false;
+ }
+ }
+
+ /**
* Sets whether the phonebook access is allowed to this device.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
*
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index c702b16..276853d 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -76,4 +76,6 @@
// System API used by framework's ShareSheet (ChooserActivity)
ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId);
+
+ boolean hasShareTargets(String packageName, String packageToCheck, int userId);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 4f7acd9..849fd03 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -635,4 +635,21 @@
}
};
}
+
+ /**
+ * Used by framework's ShareSheet (ChooserActivity.java) to check if a given package has share
+ * target definitions in it's resources.
+ *
+ * @param packageName Package to check for share targets.
+ * @return True if the package has any share target definitions, False otherwise.
+ * @hide
+ */
+ public boolean hasShareTargets(@NonNull String packageName) {
+ try {
+ return mService.hasShareTargets(mContext.getPackageName(), packageName,
+ injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index ae5b003..27f0b04 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -23,12 +23,14 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.content.ContentResolver;
import android.content.Context;
import android.metrics.LogMaker;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.provider.Settings.Secure;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
@@ -100,6 +102,7 @@
*
* @hide
*/
+ @SystemApi
public static final int AUTO_MODE_DISABLED = 0;
/**
* Auto mode value to automatically activate Night display at a specific start and end time.
@@ -110,6 +113,7 @@
*
* @hide
*/
+ @SystemApi
public static final int AUTO_MODE_CUSTOM_TIME = 1;
/**
* Auto mode value to automatically activate Night display from sunset to sunrise.
@@ -118,8 +122,45 @@
*
* @hide
*/
+ @SystemApi
public static final int AUTO_MODE_TWILIGHT = 2;
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED, COLOR_MODE_AUTOMATIC})
+ public @interface ColorMode {}
+
+ /**
+ * Color mode with natural colors.
+ *
+ * @hide
+ * @see #setColorMode(int)
+ */
+ public static final int COLOR_MODE_NATURAL = 0;
+ /**
+ * Color mode with boosted colors.
+ *
+ * @hide
+ * @see #setColorMode(int)
+ */
+ public static final int COLOR_MODE_BOOSTED = 1;
+ /**
+ * Color mode with saturated colors.
+ *
+ * @hide
+ * @see #setColorMode(int)
+ */
+ public static final int COLOR_MODE_SATURATED = 2;
+ /**
+ * Color mode with automatic colors.
+ *
+ * @hide
+ * @see #setColorMode(int)
+ */
+ public static final int COLOR_MODE_AUTOMATIC = 3;
+
private final ColorDisplayManagerInternal mManager;
private MetricsLogger mMetricsLogger;
@@ -175,6 +216,8 @@
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
public @AutoMode int getNightDisplayAutoMode() {
return mManager.getNightDisplayAutoMode();
}
@@ -199,6 +242,8 @@
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
public boolean setNightDisplayAutoMode(@AutoMode int autoMode) {
if (autoMode != AUTO_MODE_DISABLED
&& autoMode != AUTO_MODE_CUSTOM_TIME
@@ -233,6 +278,8 @@
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
public boolean setNightDisplayCustomStartTime(@NonNull LocalTime startTime) {
if (startTime == null) {
throw new IllegalArgumentException("startTime cannot be null");
@@ -263,6 +310,8 @@
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
public boolean setNightDisplayCustomEndTime(@NonNull LocalTime endTime) {
if (endTime == null) {
throw new IllegalArgumentException("endTime cannot be null");
@@ -275,6 +324,24 @@
}
/**
+ * Sets the current display color mode.
+ *
+ * @hide
+ */
+ public void setColorMode(int colorMode) {
+ mManager.setColorMode(colorMode);
+ }
+
+ /**
+ * Gets the current display color mode.
+ *
+ * @hide
+ */
+ public int getColorMode() {
+ return mManager.getColorMode();
+ }
+
+ /**
* Returns whether the device has a wide color gamut display.
*
* @hide
@@ -373,6 +440,18 @@
return mManager.getTransformCapabilities();
}
+ /**
+ * Returns whether accessibility transforms are currently enabled, which determines whether
+ * color modes are currently configurable for this device.
+ *
+ * @hide
+ */
+ public static boolean areAccessibilityTransformsEnabled(Context context) {
+ final ContentResolver cr = context.getContentResolver();
+ return Secure.getInt(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0) == 1
+ || Secure.getInt(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0) == 1;
+ }
+
private MetricsLogger getMetricsLogger() {
if (mMetricsLogger == null) {
mMetricsLogger = new MetricsLogger();
@@ -517,6 +596,22 @@
}
}
+ int getColorMode() {
+ try {
+ return mCdm.getColorMode();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void setColorMode(int colorMode) {
+ try {
+ mCdm.setColorMode(colorMode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
int getTransformCapabilities() {
try {
return mCdm.getTransformCapabilities();
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 1918fd5..30e76cf 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -38,4 +38,7 @@
boolean setNightDisplayCustomStartTime(in Time time);
Time getNightDisplayCustomEndTime();
boolean setNightDisplayCustomEndTime(in Time time);
+
+ int getColorMode();
+ void setColorMode(int colorMode);
}
\ No newline at end of file
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index a98b31a..56020b2 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -18,6 +18,7 @@
import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -33,6 +34,8 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.util.List;
/**
@@ -106,9 +109,24 @@
public static final int POWER_STATUS_TRANSIENT_TO_ON = 2;
public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3;
+ @IntDef ({
+ RESULT_SUCCESS,
+ RESULT_TIMEOUT,
+ RESULT_SOURCE_NOT_AVAILABLE,
+ RESULT_TARGET_NOT_AVAILABLE,
+ RESULT_ALREADY_IN_PROGRESS,
+ RESULT_EXCEPTION,
+ RESULT_INCORRECT_MODE,
+ RESULT_COMMUNICATION_FAILED,
+ })
+ public @interface ControlCallbackResult {}
+
+ /** Control operation is successfully handled by the framework. */
public static final int RESULT_SUCCESS = 0;
public static final int RESULT_TIMEOUT = 1;
+ /** Source device that the application is using is not available. */
public static final int RESULT_SOURCE_NOT_AVAILABLE = 2;
+ /** Target device that the application is controlling is not available. */
public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
@Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4;
@@ -394,16 +412,15 @@
/**
* Gets an object that represents an HDMI-CEC logical device of type switch on the system.
*
- * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
- * possible to communicate with other logical devices hosted in the same system if the system is
- * configured to host more than one type of HDMI-CEC logical devices.
+ * <p>Used to send HDMI control messages to other devices (e.g. TVs) through HDMI bus.
+ * It is also possible to communicate with other logical devices hosted in the same
+ * system if the system is configured to host more than one type of HDMI-CEC logical device.
*
* @return {@link HdmiSwitchClient} instance. {@code null} on failure.
- *
- * TODO(b/110094868): unhide for Q
* @hide
*/
@Nullable
+ @SystemApi
@SuppressLint("Doclava125")
public HdmiSwitchClient getSwitchClient() {
return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
@@ -412,11 +429,15 @@
/**
* Get a snapshot of the real-time status of the remote devices.
*
- * @return a list of {@link HdmiDeviceInfo} of the devices connected to the current device.
+ * <p>This only applies to devices with multiple HDMI inputs.
*
- * TODO(b/110094868): unhide for Q
+ * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices. An empty
+ * list will be returned if there is none.
+ *
* @hide
*/
+ @SystemApi
+ @Nullable
public List<HdmiDeviceInfo> getConnectedDevicesList() {
try {
return mService.getDeviceList();
@@ -426,14 +447,17 @@
}
/**
- * Power off the target device.
+ * Power off the target device by sending CEC commands.
*
- * @param deviceInfo HdmiDeviceInfo of the device to be powered off
+ * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
*
- * TODO(b/110094868): unhide for Q
+ * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered off.
+ *
* @hide
*/
+ @SystemApi
public void powerOffRemoteDevice(HdmiDeviceInfo deviceInfo) {
+ Preconditions.checkNotNull(deviceInfo);
try {
mService.powerOffRemoteDevice(
deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
@@ -443,14 +467,16 @@
}
/**
- * Power on the target device.
+ * Power on the target device by sending CEC commands.
*
- * @param deviceInfo HdmiDeviceInfo of the device to be powered on
+ * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
*
- * TODO(b/110094868): unhide for Q
+ * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered on.
+ *
* @hide
*/
public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) {
+ Preconditions.checkNotNull(deviceInfo);
try {
mService.powerOnRemoteDevice(
deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
@@ -460,14 +486,17 @@
}
/**
- * Ask the target device to be the new Active Source.
+ * Request the target device to be the new Active Source by sending CEC commands.
+ *
+ * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
*
* @param deviceInfo HdmiDeviceInfo of the target device
*
- * TODO(b/110094868): unhide for Q
* @hide
*/
- public void askRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) {
+ @SystemApi
+ public void requestRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) {
+ Preconditions.checkNotNull(deviceInfo);
try {
mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress());
} catch (RemoteException e) {
@@ -506,8 +535,13 @@
/**
* Get the physical address of the device.
*
+ * <p>Physical address needs to be automatically adjusted when devices are phyiscally or
+ * electrically added or removed from the device tree. Please see HDMI Specification Version
+ * 1.4b 8.7 Physical Address for more details on the address discovery proccess.
+ *
* @hide
*/
+ @SystemApi
public int getPhysicalAddress() {
if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
return mPhysicalAddress;
@@ -521,16 +555,19 @@
}
/**
- * Check if the target device is connected to the current device. The
- * API also returns true if the current device is the target.
+ * Check if the target remote device is connected to the current device.
+ *
+ * <p>The API also returns true if the current device is the target.
*
* @param targetDevice {@link HdmiDeviceInfo} of the target device.
- * @return true if {@code device} is directly or indirectly connected to the
+ * @return true if {@code targetDevice} is directly or indirectly
+ * connected to the current device.
*
- * TODO(b/110094868): unhide for Q
* @hide
*/
- public boolean isTargetDeviceConnected(HdmiDeviceInfo targetDevice) {
+ @SystemApi
+ public boolean isRemoteDeviceConnected(HdmiDeviceInfo targetDevice) {
+ Preconditions.checkNotNull(targetDevice);
mPhysicalAddress = getPhysicalAddress();
if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
return false;
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
index 1ac2973..a036512 100644
--- a/core/java/android/hardware/hdmi/HdmiSwitchClient.java
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -15,24 +15,30 @@
*/
package android.hardware.hdmi;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiControlManager.ControlCallbackResult;
+import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
/**
- * HdmiSwitchClient represents HDMI-CEC logical device of type Switch in the Android system which
- * acts as switch.
+ * An {@code HdmiSwitchClient} represents a HDMI-CEC switch device.
*
- * <p>HdmiSwitchClient has a CEC device type of HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
- * but it is used by all Android TV devices that have multiple HDMI inputs,
- * even if it is not a "pure" swicth and has another device type like TV or Player.
+ * <p>HdmiSwitchClient has a CEC device type of HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH, but it is
+ * used by all Android devices that have multiple HDMI inputs, even if it is not a "pure" swicth
+ * and has another device type like TV or Player.
*
* @hide
- * TODO(b/110094868): unhide and add @SystemApi for Q
*/
+@SystemApi
public class HdmiSwitchClient extends HdmiClient {
private static final String TAG = "HdmiSwitchClient";
@@ -41,17 +47,15 @@
super(service);
}
- private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+ private static IHdmiControlCallback getCallbackWrapper(final OnSelectListener listener) {
return new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
- callback.onComplete(result);
+ listener.onSelect(result);
}
};
}
- /** @hide */
- // TODO(b/110094868): unhide for Q
@Override
public int getDeviceType() {
return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
@@ -61,20 +65,17 @@
* Selects a CEC logical device to be a new active source.
*
* @param logicalAddress logical address of the device to select
- * @param callback callback to get the result with
- * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ * @param listener listener to get the result with
*
* @hide
- * TODO(b/110094868): unhide and add @SystemApi for Q
*/
- public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback must not be null.");
- }
+ public void selectDevice(int logicalAddress, @NonNull OnSelectListener listener) {
+ Preconditions.checkNotNull(listener);
try {
- mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+ mService.deviceSelect(logicalAddress, getCallbackWrapper(listener));
} catch (RemoteException e) {
Log.e(TAG, "failed to select device: ", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -82,20 +83,83 @@
* Selects a HDMI port to be a new route path.
*
* @param portId HDMI port to select
- * @param callback callback to get the result with
- * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ * @see {@link android.media.tv.TvInputHardwareInfo#getHdmiPortId()}
+ * to get portId of a specific TV Input.
+ * @param listener listener to get the result with
*
* @hide
- * TODO(b/110094868): unhide and add @SystemApi for Q
*/
- public void portSelect(int portId, @NonNull SelectCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("Callback must not be null");
- }
+ @SystemApi
+ public void selectPort(int portId, @NonNull OnSelectListener listener) {
+ Preconditions.checkNotNull(listener);
try {
- mService.portSelect(portId, getCallbackWrapper(callback));
+ mService.portSelect(portId, getCallbackWrapper(listener));
} catch (RemoteException e) {
Log.e(TAG, "failed to select port: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Selects a CEC logical device to be a new active source.
+ *
+ * @param logicalAddress logical address of the device to select
+ * @param executor executor to allow the develper to specify the thread upon which the listeners
+ * will be invoked
+ * @param listener listener to get the result with
+ *
+ * @hide
+ */
+ public void selectDevice(
+ int logicalAddress,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnSelectListener listener) {
+ Preconditions.checkNotNull(listener);
+ try {
+ mService.deviceSelect(logicalAddress,
+ new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ Binder.withCleanCallingIdentity(
+ () -> executor.execute(() -> listener.onSelect(result)));
+ }
+ }
+ );
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select device: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Selects a HDMI port to be a new route path.
+ *
+ * @param portId HDMI port to select
+ * @param executor executor to allow the develper to specify the thread upon which the listeners
+ * will be invoked
+ * @param listener listener to get the result with
+ *
+ * @hide
+ */
+ @SystemApi
+ public void selectPort(
+ int portId,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnSelectListener listener) {
+ Preconditions.checkNotNull(listener);
+ try {
+ mService.portSelect(portId,
+ new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ Binder.withCleanCallingIdentity(
+ () -> executor.execute(() -> listener.onSelect(result)));
+ }
+ }
+ );
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select port: ", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -105,10 +169,9 @@
* <p>This only applies to device with multiple HDMI inputs
*
* @return list of {@link HdmiDeviceInfo} for connected CEC devices. Empty list is returned if
- * there is none.
+ * there is none.
*
* @hide
- * TODO(b/110094868): unhide and add @SystemApi for Q
*/
public List<HdmiDeviceInfo> getDeviceList() {
try {
@@ -120,18 +183,19 @@
}
/**
- * Callback interface used to get the result of {@link #deviceSelect} or {@link #portSelect}.
+ * Listener interface used to get the result of {@link #deviceSelect} or {@link #portSelect}.
*
* @hide
- * TODO(b/110094868): unhide and add @SystemApi for Q
*/
- public interface SelectCallback {
+ @SystemApi
+ public interface OnSelectListener {
/**
* Called when the operation is finished.
*
- * @param result the result value of {@link #deviceSelect} or {@link #portSelect}.
+ * @param result callback result.
+ * @see {@link ControlCallbackResult}
*/
- void onComplete(int result);
+ void onSelect(@ControlCallbackResult int result);
}
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 1030694..37b25c8 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -179,19 +179,24 @@
return;
case DO_START_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
- final int missingMethods = msg.arg1;
- final boolean restarting = msg.arg2 != 0;
final IBinder startInputToken = (IBinder) args.arg1;
final IInputContext inputContext = (IInputContext) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4;
+ SomeArgs moreArgs = (SomeArgs) args.arg5;
final InputConnection ic = inputContext != null
? new InputConnectionWrapper(
- mTarget, inputContext, missingMethods, isUnbindIssued) : null;
+ mTarget, inputContext, moreArgs.argi3, isUnbindIssued)
+ : null;
info.makeCompatible(mTargetSdkVersion);
- inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */,
- startInputToken);
+ inputMethod.dispatchStartInputWithToken(
+ ic,
+ info,
+ moreArgs.argi1 == 1 /* restarting */,
+ startInputToken,
+ moreArgs.argi2 == 1 /* shouldPreRenderIme */);
args.recycle();
+ moreArgs.recycle();
return;
}
case DO_CREATE_SESSION: {
@@ -291,14 +296,17 @@
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
@InputConnectionInspector.MissingMethodFlags final int missingMethods,
- EditorInfo attribute, boolean restarting) {
+ EditorInfo attribute, boolean restarting, boolean shouldPreRenderIme) {
if (mIsUnbindIssued == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mIsUnbindIssued = new AtomicBoolean();
}
- mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT,
- missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute,
- mIsUnbindIssued));
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = restarting ? 1 : 0;
+ args.argi2 = shouldPreRenderIme ? 1 : 0;
+ args.argi3 = missingMethods;
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO(
+ DO_START_INPUT, startInputToken, inputContext, attribute, mIsUnbindIssued, args));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 38ddc16..5b3ad77 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -346,6 +346,13 @@
*/
public static final int IME_VISIBLE = 0x2;
+ /**
+ * @hide
+ * The IME is active and ready with views but set invisible.
+ * This flag cannot be combined with {@link #IME_VISIBLE}.
+ */
+ public static final int IME_INVISIBLE = 0x4;
+
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
@@ -362,10 +369,19 @@
View mRootView;
SoftInputWindow mWindow;
boolean mInitialized;
- boolean mWindowCreated;
- boolean mWindowVisible;
- boolean mWindowWasVisible;
+ boolean mViewsCreated;
+ // IME views visibility.
+ boolean mDecorViewVisible;
+ boolean mDecorViewWasVisible;
boolean mInShowWindow;
+ // True if pre-rendering of IME views/window is supported.
+ boolean mCanPreRender;
+ // If IME is pre-rendered.
+ boolean mIsPreRendered;
+ // IME window visibility.
+ // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
+ boolean mWindowVisible;
+
ViewGroup mFullscreenArea;
FrameLayout mExtractFrame;
FrameLayout mCandidatesFrame;
@@ -552,15 +568,18 @@
*/
@MainThread
@Override
- public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
+ public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
mPrivOps.reportStartInput(startInputToken);
- // This needs to be dispatched to interface methods rather than doStartInput().
- // Otherwise IME developers who have overridden those interface methods will lose
- // notifications.
- super.dispatchStartInputWithToken(inputConnection, editorInfo, restarting,
- startInputToken);
+ mCanPreRender = shouldPreRenderIme;
+ if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender);
+
+ if (restarting) {
+ restartInput(inputConnection, editorInfo);
+ } else {
+ startInput(inputConnection, editorInfo);
+ }
}
/**
@@ -570,14 +589,27 @@
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
- boolean wasVis = isInputViewShown();
- mShowInputFlags = 0;
- mShowInputRequested = false;
- doHideWindow();
+ final boolean wasVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ if (mIsPreRendered) {
+ // TODO: notify visibility to insets consumer.
+ if (DEBUG) {
+ Log.v(TAG, "Making IME window invisible");
+ }
+ setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+ } else {
+ mShowInputFlags = 0;
+ mShowInputRequested = false;
+ doHideWindow();
+ }
+ final boolean isVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
- resultReceiver.send(wasVis != isInputViewShown()
+ resultReceiver.send(visibilityChanged
? InputMethodManager.RESULT_HIDDEN
- : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
@@ -589,17 +621,28 @@
@Override
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "showSoftInput()");
- boolean wasVis = isInputViewShown();
+ final boolean wasVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
- showWindow(true);
+ if (mIsPreRendered) {
+ // TODO: notify visibility to insets consumer.
+ if (DEBUG) {
+ Log.v(TAG, "Making IME window visible");
+ }
+ onPreRenderedWindowVisibilityChanged(true /* setVisible */);
+ } else {
+ showWindow(true);
+ }
}
// If user uses hard keyboard, IME button should always be shown.
- setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
-
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ final boolean isVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
- resultReceiver.send(wasVis != isInputViewShown()
+ resultReceiver.send(visibilityChanged
? InputMethodManager.RESULT_SHOWN
- : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
@@ -972,7 +1015,7 @@
void initViews() {
mInitialized = false;
- mWindowCreated = false;
+ mViewsCreated = false;
mShowInputRequested = false;
mShowInputFlags = 0;
@@ -1046,7 +1089,7 @@
}
private void resetStateForNewConfiguration() {
- boolean visible = mWindowVisible;
+ boolean visible = mDecorViewVisible;
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
CompletionInfo[] completions = mCurCompletions;
@@ -1129,7 +1172,7 @@
return;
}
mBackDisposition = disposition;
- setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
/**
@@ -1380,7 +1423,7 @@
mExtractFrame.setVisibility(View.GONE);
}
updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
- if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
+ if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
: com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
@@ -1439,7 +1482,7 @@
*/
public void updateInputViewShown() {
boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
- if (mIsInputViewShown != isShown && mWindowVisible) {
+ if (mIsInputViewShown != isShown && mDecorViewVisible) {
mIsInputViewShown = isShown;
mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
if (mInputView == null) {
@@ -1458,14 +1501,14 @@
public boolean isShowInputRequested() {
return mShowInputRequested;
}
-
+
/**
* Return whether the soft input view is <em>currently</em> shown to the
* user. This is the state that was last determined and
* applied by {@link #updateInputViewShown()}.
*/
public boolean isInputViewShown() {
- return mIsInputViewShown && mWindowVisible;
+ return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible;
}
/**
@@ -1499,7 +1542,7 @@
*/
public void setCandidatesViewShown(boolean shown) {
updateCandidatesVisibility(shown);
- if (!mShowInputRequested && mWindowVisible != shown) {
+ if (!mShowInputRequested && mDecorViewVisible != shown) {
// If we are being asked to show the candidates view while the app
// has not asked for the input view to be shown, then we need
// to update whether the window is shown.
@@ -1804,7 +1847,8 @@
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
- + " mWindowCreated=" + mWindowCreated
+ + " mViewsCreated=" + mViewsCreated
+ + " mDecorViewVisible=" + mDecorViewVisible
+ " mWindowVisible=" + mWindowVisible
+ " mInputStarted=" + mInputStarted
+ " mShowInputFlags=" + mShowInputFlags);
@@ -1814,18 +1858,42 @@
return;
}
- mWindowWasVisible = mWindowVisible;
+ mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
- showWindowInner(showInput);
- mWindowWasVisible = true;
+ boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible;
+ final int previousImeWindowStatus =
+ (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
+ ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
+ startViews(prepareWindow(showInput));
+ final int nextImeWindowStatus = mapToImeWindowStatus();
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
+ }
+
+ // compute visibility
+ onWindowShown();
+ mIsPreRendered = mCanPreRender;
+ if (mIsPreRendered) {
+ onPreRenderedWindowVisibilityChanged(true /* setVisible */);
+ } else {
+ // Pre-rendering not supported.
+ if (DEBUG) Log.d(TAG, "No pre-rendering supported");
+ mWindowVisible = true;
+ }
+
+ // request draw for the IME surface.
+ // When IME is not pre-rendered, this will actually show the IME.
+ if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
+ }
+ mDecorViewWasVisible = true;
mInShowWindow = false;
}
- void showWindowInner(boolean showInput) {
+ private boolean prepareWindow(boolean showInput) {
boolean doShowInput = false;
- final int previousImeWindowStatus =
- (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
- mWindowVisible = true;
+ mDecorViewVisible = true;
if (!mShowInputRequested && mInputStarted && showInput) {
doShowInput = true;
mShowInputRequested = true;
@@ -1836,8 +1904,8 @@
updateFullscreenMode();
updateInputViewShown();
- if (!mWindowCreated) {
- mWindowCreated = true;
+ if (!mViewsCreated) {
+ mViewsCreated = true;
initialize();
if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
View v = onCreateCandidatesView();
@@ -1846,6 +1914,10 @@
setCandidatesView(v);
}
}
+ return doShowInput;
+ }
+
+ private void startViews(boolean doShowInput) {
if (mShowInputRequested) {
if (!mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
@@ -1857,29 +1929,26 @@
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, false);
}
-
- if (doShowInput) {
- startExtractingText(false);
- }
+ if (doShowInput) startExtractingText(false);
+ }
- final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
- if (previousImeWindowStatus != nextImeWindowStatus) {
- setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
- }
- if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
- if (DEBUG) Log.v(TAG, "showWindow: showing!");
+ private void onPreRenderedWindowVisibilityChanged(boolean setVisible) {
+ mWindowVisible = setVisible;
+ mShowInputFlags = setVisible ? mShowInputFlags : 0;
+ mShowInputRequested = setVisible;
+ mDecorViewVisible = setVisible;
+ if (setVisible) {
onWindowShown();
- mWindow.show();
}
}
- private void finishViews() {
+ private void finishViews(boolean finishingInput) {
if (mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
- onFinishInputView(false);
+ onFinishInputView(finishingInput);
} else if (mCandidatesViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
- onFinishCandidatesView(false);
+ onFinishCandidatesView(finishingInput);
}
mInputViewStarted = false;
mCandidatesViewStarted = false;
@@ -1891,12 +1960,15 @@
}
public void hideWindow() {
- finishViews();
- if (mWindowVisible) {
+ if (DEBUG) Log.v(TAG, "CALL: hideWindow");
+ mIsPreRendered = false;
+ mWindowVisible = false;
+ finishViews(false /* finishingInput */);
+ if (mDecorViewVisible) {
mWindow.hide();
- mWindowVisible = false;
+ mDecorViewVisible = false;
onWindowHidden();
- mWindowWasVisible = false;
+ mDecorViewWasVisible = false;
}
updateFullscreenMode();
}
@@ -1956,15 +2028,8 @@
}
void doFinishInput() {
- if (mInputViewStarted) {
- if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
- onFinishInputView(true);
- } else if (mCandidatesViewStarted) {
- if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
- onFinishCandidatesView(true);
- }
- mInputViewStarted = false;
- mCandidatesViewStarted = false;
+ if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
+ finishViews(true /* finishingInput */);
if (mInputStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
onFinishInput();
@@ -1984,7 +2049,7 @@
initialize();
if (DEBUG) Log.v(TAG, "CALL: onStartInput");
onStartInput(attribute, restarting);
- if (mWindowVisible) {
+ if (mDecorViewVisible) {
if (mShowInputRequested) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
mInputViewStarted = true;
@@ -1995,6 +2060,31 @@
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, restarting);
}
+ } else if (mCanPreRender && mInputEditorInfo != null && mStartedInputConnection != null) {
+ // Pre-render IME views and window when real EditorInfo is available.
+ // pre-render IME window and keep it invisible.
+ if (DEBUG) Log.v(TAG, "Pre-Render IME for " + mInputEditorInfo.fieldName);
+ if (mInShowWindow) {
+ Log.w(TAG, "Re-entrance in to showWindow");
+ return;
+ }
+
+ mDecorViewWasVisible = mDecorViewVisible;
+ mInShowWindow = true;
+ startViews(prepareWindow(true /* showInput */));
+
+ // compute visibility
+ mIsPreRendered = true;
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+
+ // request draw for the IME surface.
+ // When IME is not pre-rendered, this will actually show the IME.
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
+ mDecorViewWasVisible = true;
+ mInShowWindow = false;
+ } else {
+ mIsPreRendered = false;
}
}
@@ -2153,7 +2243,7 @@
// consume the back key.
if (doIt) requestHideSelf(0);
return true;
- } else if (mWindowVisible) {
+ } else if (mDecorViewVisible) {
if (mCandidatesVisibility == View.VISIBLE) {
// If we are showing candidates even if no input area, then
// hide them.
@@ -2180,7 +2270,6 @@
return mExtractEditText;
}
-
/**
* Called back when a {@link KeyEvent} is forwarded from the target application.
*
@@ -2893,8 +2982,11 @@
inputContentInfo.setUriToken(uriToken);
}
- private static int mapToImeWindowStatus(boolean isInputViewShown) {
- return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0);
+ private int mapToImeWindowStatus() {
+ return IME_ACTIVE
+ | (isInputViewShown()
+ ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE)
+ : IME_VISIBLE) : 0);
}
/**
@@ -2904,9 +2996,10 @@
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method service state for " + this + ":");
- p.println(" mWindowCreated=" + mWindowCreated);
- p.println(" mWindowVisible=" + mWindowVisible
- + " mWindowWasVisible=" + mWindowWasVisible
+ p.println(" mViewsCreated=" + mViewsCreated);
+ p.println(" mDecorViewVisible=" + mDecorViewVisible
+ + " mDecorViewWasVisible=" + mDecorViewWasVisible
+ + " mWindowVisible=" + mWindowVisible
+ " mInShowWindow=" + mInShowWindow);
p.println(" Configuration=" + getResources().getConfiguration());
p.println(" mToken=" + mToken);
@@ -2926,6 +3019,8 @@
p.println(" mShowInputRequested=" + mShowInputRequested
+ " mLastShowInputRequested=" + mLastShowInputRequested
+ + " mCanPreRender=" + mCanPreRender
+ + " mIsPreRendered=" + mIsPreRendered
+ " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
p.println(" mCandidatesVisibility=" + mCandidatesVisibility
+ " mFullscreenApplied=" + mFullscreenApplied
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 243b0eba..5bb24ba 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1014,20 +1014,14 @@
* to remove an existing always-on VPN configuration.
* @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
* {@code false} otherwise.
- * @param lockdownWhitelist The list of packages that are allowed to access network directly
- * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
- * this method must be called when a package that should be whitelisted is installed or
- * uninstalled.
* @return {@code true} if the package is set as always-on VPN controller;
* {@code false} otherwise.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
- boolean lockdownEnabled, @Nullable List<String> lockdownWhitelist) {
+ boolean lockdownEnabled) {
try {
- return mService.setAlwaysOnVpnPackage(
- userId, vpnPackage, lockdownEnabled, lockdownWhitelist);
+ return mService.setAlwaysOnVpnPackage(userId, vpnPackage, lockdownEnabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1042,7 +1036,6 @@
* or {@code null} if none is set.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
public String getAlwaysOnVpnPackageForUser(int userId) {
try {
return mService.getAlwaysOnVpnPackage(userId);
@@ -1052,36 +1045,6 @@
}
/**
- * @return whether always-on VPN is in lockdown mode.
- *
- * @hide
- **/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
- public boolean isVpnLockdownEnabled(int userId) {
- try {
- return mService.isVpnLockdownEnabled(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- }
-
- /**
- * @return the list of packages that are allowed to access network when always-on VPN is in
- * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
- *
- * @hide
- **/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
- public List<String> getVpnLockdownWhitelist(int userId) {
- try {
- return mService.getVpnLockdownWhitelist(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Returns details about the currently active default data network
* for a given uid. This is for internal use only to avoid spying
* other apps.
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index fd7360f..e97060a 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -125,11 +125,8 @@
boolean updateLockdownVpn();
boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
- boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
- in List<String> lockdownWhitelist);
+ boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown);
String getAlwaysOnVpnPackage(int userId);
- boolean isVpnLockdownEnabled(int userId);
- List<String> getVpnLockdownWhitelist(int userId);
int checkMobileProvisioning(int suggestedTimeOutMs);
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 518528d..d463b44 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -75,13 +75,25 @@
int BUGREPORT_ERROR_USER_DENIED_CONSENT =
IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT;
+ /** The request to get user consent timed out. */
+ int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT =
+ IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT;
+
/**
* Called when taking bugreport resulted in an error.
*
* @param errorCode the error that occurred. Possible values are
* {@code BUGREPORT_ERROR_INVALID_INPUT},
* {@code BUGREPORT_ERROR_RUNTIME},
- * {@code BUGREPORT_ERROR_USER_DENIED_CONSENT}.
+ * {@code BUGREPORT_ERROR_USER_DENIED_CONSENT},
+ * {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT}.
+ *
+ * <p>If {@code BUGREPORT_ERROR_USER_DENIED_CONSENT} is passed, then the user did not
+ * consent to sharing the bugreport with the calling app.
+ *
+ * <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed
+ * out, but the bugreport could be available in the internal directory of dumpstate for
+ * manual retrieval.
*/
void onError(@BugreportErrorCode int errorCode);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 5bf9095..efcad3e 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -369,26 +369,6 @@
}
/**
- * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
- * True: Rules file was loaded.
- * False: Rules file was *not* loaded.
- */
- private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
- // b/121153494
- // Skip APK rules file checking.
- if (!DEBUG) {
- Log.v(TAG, "Skipping loading the rules file.");
- // Fill in some default values for now, so the loader can get an answer when it asks.
- // Most importantly, we need to indicate which app we are init'ing and what the
- // developer options for it are so we can turn on ANGLE if needed.
- setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
- return true;
- }
-
- return false;
- }
-
- /**
* Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
* True: APK rules file was loaded.
* False: APK rules file was *not* loaded.
@@ -425,16 +405,57 @@
}
/**
+ * Pull ANGLE whitelist from GlobalSettings and compare against current package
+ */
+ private boolean checkAngleWhitelist(Bundle bundle, String packageName) {
+ List<String> angleWhitelist =
+ getGlobalSettingsString(bundle,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST);
+
+ return angleWhitelist.contains(packageName);
+ }
+
+ /**
* Pass ANGLE details down to trigger enable logic
*/
public void setupAngle(Context context, Bundle bundle, String packageName) {
- String devOptIn = getDriverForPkg(bundle, packageName);
+ if (packageName.isEmpty()) {
+ Log.v(TAG, "No package name available yet, skipping ANGLE setup");
+ return;
+ }
+ String devOptIn = getDriverForPkg(bundle, packageName);
if (DEBUG) {
Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + devOptIn + "'");
}
+ // We only need to check rules if the app is whitelisted or the developer has
+ // explicitly chosen something other than default driver.
+ //
+ // The whitelist will be generated by the ANGLE APK at both boot time and
+ // ANGLE update time. It will only include apps mentioned in the rules file.
+ //
+ // If the user has set the developer option to something other than default,
+ // we need to call setupAngleRulesApk() with the package name and the developer
+ // option value (native/angle/other). Then later when we are actually trying to
+ // load a driver, GraphicsEnv::shouldUseAngle() has seen the package name before
+ // and can confidently answer yes/no based on the previously set developer
+ // option value.
+ boolean whitelisted = checkAngleWhitelist(bundle, packageName);
+ boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT));
+ boolean rulesCheck = (whitelisted || !defaulted);
+ if (!rulesCheck) {
+ return;
+ }
+
+ if (whitelisted) {
+ Log.v(TAG, "ANGLE whitelist includes " + packageName);
+ }
+ if (!defaulted) {
+ Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
+ }
+
String anglePkgName = getAnglePackageName(context);
if (anglePkgName.isEmpty()) {
Log.e(TAG, "Failed to find ANGLE package.");
@@ -466,12 +487,6 @@
return;
}
- // b/121153494
- if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
- // We setup ANGLE with defaults, so we're done here.
- return;
- }
-
if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
// We setup ANGLE with rules from the APK, so we're done here.
return;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 63912ec..630bd2e 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -54,6 +54,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.io.UncheckedIOException;
import java.net.DatagramSocket;
import java.net.Socket;
import java.nio.ByteOrder;
@@ -393,26 +394,41 @@
* @param socket The Socket whose FileDescriptor is used to create
* a new ParcelFileDescriptor.
*
- * @return A new ParcelFileDescriptor with the FileDescriptor of the
- * specified Socket.
+ * @return A new ParcelFileDescriptor with a duped copy of the
+ * FileDescriptor of the specified Socket.
+ *
+ * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
public static ParcelFileDescriptor fromSocket(Socket socket) {
FileDescriptor fd = socket.getFileDescriptor$();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
+ try {
+ return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
/**
- * Create a new ParcelFileDescriptor from the specified DatagramSocket.
+ * Create a new ParcelFileDescriptor from the specified DatagramSocket. The
+ * new ParcelFileDescriptor holds a dup of the original FileDescriptor in
+ * the DatagramSocket, so you must still close the DatagramSocket as well
+ * as the new ParcelFileDescriptor.
*
* @param datagramSocket The DatagramSocket whose FileDescriptor is used
* to create a new ParcelFileDescriptor.
*
- * @return A new ParcelFileDescriptor with the FileDescriptor of the
- * specified DatagramSocket.
+ * @return A new ParcelFileDescriptor with a duped copy of the
+ * FileDescriptor of the specified Socket.
+ *
+ * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
FileDescriptor fd = datagramSocket.getFileDescriptor$();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
+ try {
+ return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
/**
@@ -542,7 +558,7 @@
}
file.deactivate();
FileDescriptor fd = file.getFileDescriptor();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
+ return fd != null ? ParcelFileDescriptor.dup(fd) : null;
}
/**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index cd823a9..148dd91 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -181,6 +181,23 @@
String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
}
+ /**
+ * Namespace for storage-related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface Storage {
+ String NAMESPACE = "storage";
+
+ /**
+ * If {@code 1}, enables the isolated storage feature. If {@code -1},
+ * disables the isolated storage feature. If {@code 0}, uses the default
+ * value from the build system.
+ */
+ String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
+ }
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0a592d4..d840e3c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -59,6 +59,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
+import android.hardware.display.ColorDisplayManager;
import android.location.LocationManager;
import android.media.AudioFormat;
import android.net.ConnectivityManager;
@@ -89,7 +90,6 @@
import android.view.inputmethod.InputMethodSystemProperty;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.ColorDisplayController;
import com.android.internal.widget.ILockSettings;
import java.io.IOException;
@@ -3239,8 +3239,8 @@
private static final Validator DISPLAY_COLOR_MODE_VALIDATOR =
new SettingsValidators.InclusiveIntegerRangeValidator(
- ColorDisplayController.COLOR_MODE_NATURAL,
- ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ ColorDisplayManager.COLOR_MODE_NATURAL,
+ ColorDisplayManager.COLOR_MODE_AUTOMATIC);
/**
* The amount of time in milliseconds before the device goes to sleep or begins
@@ -5800,16 +5800,6 @@
public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown";
/**
- * Comma separated list of packages that are allowed to access the network when VPN is in
- * lockdown mode but not running.
- * @see #ALWAYS_ON_VPN_LOCKDOWN
- *
- * @hide
- */
- public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST =
- "always_on_vpn_lockdown_whitelist";
-
- /**
* Whether applications can be installed for this user via the system's
* {@link Intent#ACTION_INSTALL_PACKAGE} mechanism.
*
@@ -7812,6 +7802,9 @@
* or an activity that handles ACTION_ASSIST, or empty which means using the default
* handling.
*
+ * <p>This should be set indirectly by setting the {@link
+ * android.app.role.RoleManager#ROLE_ASSISTANT assistant role}.
+ *
* @hide
*/
@UnsupportedAppUsage
@@ -8246,6 +8239,16 @@
private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR;
/**
+ * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
+ * swipe).
+ *
+ * @hide
+ */
+ public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl";
+
+ private static final Validator NOTIFICATION_DISMISS_RTL_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* Comma separated list of QS tiles that have been auto-added already.
* @hide
*/
@@ -8547,6 +8550,7 @@
ASSIST_GESTURE_WAKE_ENABLED,
VR_DISPLAY_MODE,
NOTIFICATION_BADGING,
+ NOTIFICATION_DISMISS_RTL,
QS_AUTO_ADDED_TILES,
SCREENSAVER_ENABLED,
SCREENSAVER_COMPONENTS,
@@ -8709,6 +8713,7 @@
VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR);
VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR);
+ VALIDATORS.put(NOTIFICATION_DISMISS_RTL, NOTIFICATION_DISMISS_RTL_VALIDATOR);
VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR);
VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR);
VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR);
@@ -12148,6 +12153,13 @@
"angle_gl_driver_selection_values";
/**
+ * List of package names that should check ANGLE rules
+ * @hide
+ */
+ public static final String GLOBAL_SETTINGS_ANGLE_WHITELIST =
+ "angle_whitelist";
+
+ /**
* Game Update Package global preference for all Apps.
* 0 = Default
* 1 = All Apps use Game Update Package
@@ -13018,28 +13030,6 @@
"sms_access_restriction_enabled";
/**
- * If set to 1, an app must have the READ_PRIVILEGED_PHONE_STATE permission (or be a device
- * / profile owner with the READ_PHONE_STATE permission) to access device identifiers.
- *
- * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
- *
- * @hide
- */
- public static final String PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED =
- "privileged_device_identifier_check_enabled";
-
- /**
- * If set to 1, an app that is targeting Q and does not meet the new requirements to access
- * device identifiers will receive a SecurityException.
- *
- * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
- *
- * @hide
- */
- public static final String PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED =
- "privileged_device_identifier_target_q_behavior_enabled";
-
- /**
* If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
* permission check for 3P apps.
*
@@ -13062,6 +13052,17 @@
"privileged_device_identifier_non_priv_check_relaxed";
/**
+ * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+ * permission check for preloaded privileged apps.
+ *
+ * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+ *
+ * @hide
+ */
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED =
+ "privileged_device_identifier_priv_check_relaxed";
+
+ /**
* If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
* and restoring to lower version of platform API will be skipped.
*
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 0edcb3d..2984ef6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -53,9 +53,9 @@
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
DEFAULT_FLAGS.put("settings_slice_injection", "false");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
- DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
- DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
- DEFAULT_FLAGS.put("settings_wifi_sharing", "false");
+ DEFAULT_FLAGS.put("settings_wifi_dpp", "true");
+ DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true");
+ DEFAULT_FLAGS.put("settings_wifi_sharing", "true");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SAFETY_HUB, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 529776e..a6af1a2 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,7 +16,10 @@
package android.view;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.indexOf;
import android.annotation.IntDef;
@@ -124,9 +127,10 @@
@Nullable @InsetSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
+ boolean[] typeVisibilityMap = new boolean[SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
- if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
+ if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
&& legacyContentInsets != null && legacyStableInsets != null) {
WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets);
WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets);
@@ -136,22 +140,29 @@
if (source == null) {
continue;
}
+ if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
+ && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR)) {
+ typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
+ continue;
+ }
+
processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
- typeSideMap);
+ typeSideMap, typeVisibilityMap);
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
if (source.getType() != TYPE_IME) {
processSource(source, relativeFrameMax, true /* ignoreVisibility */,
- typeMaxInsetsMap, null /* typeSideMap */);
+ typeMaxInsetsMap, null /* typeSideMap */, null /* typeVisibilityMap */);
}
}
- return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, isScreenRound,
+ return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
alwaysConsumeNavBar, cutout);
}
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
- Insets[] typeInsetsMap, @Nullable @InsetSide SparseIntArray typeSideMap) {
+ Insets[] typeInsetsMap, @Nullable @InsetSide SparseIntArray typeSideMap,
+ @Nullable boolean[] typeVisibilityMap) {
Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
int index = indexOf(toPublicType(source.getType()));
@@ -162,6 +173,10 @@
typeInsetsMap[index] = Insets.max(existing, insets);
}
+ if (typeVisibilityMap != null) {
+ typeVisibilityMap[index] = source.isVisible();
+ }
+
if (typeSideMap != null && !Insets.NONE.equals(insets)) {
@InsetSide int insetSide = getInsetSide(insets);
if (insetSide != INSET_SIDE_UNKNWON) {
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
index 3c9ce78..6f5a85d 100644
--- a/core/java/android/view/RemoteAnimationAdapter.java
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -18,7 +18,6 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityOptions;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -52,6 +51,7 @@
private final IRemoteAnimationRunner mRunner;
private final long mDuration;
private final long mStatusBarTransitionDelay;
+ private final boolean mChangeNeedsSnapshot;
/** @see #getCallingPid */
private int mCallingPid;
@@ -59,21 +59,31 @@
/**
* @param runner The interface that gets notified when we actually need to start the animation.
* @param duration The duration of the animation.
+ * @param changeNeedsSnapshot For change transitions, whether this should create a snapshot by
+ * screenshotting the task.
* @param statusBarTransitionDelay The desired delay for all visual animations in the
* status bar caused by this app animation in millis.
*/
@UnsupportedAppUsage
public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
- long statusBarTransitionDelay) {
+ long statusBarTransitionDelay, boolean changeNeedsSnapshot) {
mRunner = runner;
mDuration = duration;
+ mChangeNeedsSnapshot = changeNeedsSnapshot;
mStatusBarTransitionDelay = statusBarTransitionDelay;
}
+ @UnsupportedAppUsage
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay) {
+ this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
+ }
+
public RemoteAnimationAdapter(Parcel in) {
mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
mDuration = in.readLong();
mStatusBarTransitionDelay = in.readLong();
+ mChangeNeedsSnapshot = in.readBoolean();
}
public IRemoteAnimationRunner getRunner() {
@@ -88,6 +98,10 @@
return mStatusBarTransitionDelay;
}
+ public boolean getChangeNeedsSnapshot() {
+ return mChangeNeedsSnapshot;
+ }
+
/**
* To be called by system_server to keep track which pid is running this animation.
*/
@@ -112,6 +126,7 @@
dest.writeStrongInterface(mRunner);
dest.writeLong(mDuration);
dest.writeLong(mStatusBarTransitionDelay);
+ dest.writeBoolean(mChangeNeedsSnapshot);
}
public static final Creator<RemoteAnimationAdapter> CREATOR
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 483280e..f1dfc1c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3985,6 +3985,15 @@
public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
/**
+ * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain the default
+ * long press action will be inhibited. However, to account for the possibility of incorrect
+ * classification, the default long press timeout will instead be increased for some situations
+ * by the following factor.
+ * Likewise, the touch slop for allowing long press will be increased when gesture is uncertain.
+ */
+ private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
+
+ /**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -9154,7 +9163,7 @@
ContentCaptureSession session = null;
if (mParent instanceof View) {
- session = ((View) mParent).getContentCaptureSession();
+ session = ((View) mParent).getContentCaptureSession(ccm);
}
return session != null ? session : ccm.getMainContentCaptureSession();
@@ -14780,8 +14789,27 @@
drawableHotspotChanged(x, y);
}
+ final int motionClassification = event.getClassification();
+ final boolean ambiguousGesture =
+ motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
+ int touchSlop = mTouchSlop;
+ if (ambiguousGesture && hasPendingLongPressCallback()) {
+ if (!pointInView(x, y, touchSlop)) {
+ // The default action here is to cancel long press. But instead, we
+ // just extend the timeout here, in case the classification
+ // stays ambiguous.
+ removeLongPressCallback();
+ long delay = ViewConfiguration.getLongPressTimeout()
+ * AMBIGUOUS_GESTURE_MULTIPLIER;
+ // Subtract the time already spent
+ delay -= event.getEventTime() - event.getDownTime();
+ checkForLongClick(delay, x, y);
+ }
+ touchSlop *= AMBIGUOUS_GESTURE_MULTIPLIER;
+ }
+
// Be lenient about moving outside of buttons
- if (!pointInView(x, y, mTouchSlop)) {
+ if (!pointInView(x, y, touchSlop)) {
// Outside button
// Remove any future long press/tap checks
removeTapCallback();
@@ -14791,6 +14819,15 @@
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
+
+ final boolean deepPress =
+ motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
+ if (deepPress && hasPendingLongPressCallback()) {
+ // process the long click action immediately
+ removeLongPressCallback();
+ checkForLongClick(0 /* send immediately */, x, y);
+ }
+
break;
}
@@ -14825,6 +14862,21 @@
}
/**
+ * Return true if the long press callback is scheduled to run sometime in the future.
+ * Return false if there is no scheduled long press callback at the moment.
+ */
+ private boolean hasPendingLongPressCallback() {
+ if (mPendingCheckForLongPress == null) {
+ return false;
+ }
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo == null) {
+ return false;
+ }
+ return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress);
+ }
+
+ /**
* Remove the pending click action
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index e808830..c1536ae 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -66,6 +66,7 @@
private final Insets[] mTypeInsetsMap;
private final Insets[] mTypeMaxInsetsMap;
+ private final boolean[] mTypeVisibilityMap;
@Nullable private Rect mTempRect;
private final boolean mIsRound;
@@ -106,6 +107,7 @@
public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
+ createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
isRound, alwaysConsumeNavBar, displayCutout);
}
@@ -122,7 +124,9 @@
* @hide
*/
public WindowInsets(@Nullable Insets[] typeInsetsMap,
- @Nullable Insets[] typeMaxInsetsMap, boolean isRound,
+ @Nullable Insets[] typeMaxInsetsMap,
+ boolean[] typeVisibilityMap,
+ boolean isRound,
boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
@@ -134,6 +138,7 @@
? new Insets[SIZE]
: typeMaxInsetsMap.clone();
+ mTypeVisibilityMap = typeVisibilityMap;
mIsRound = isRound;
mAlwaysConsumeNavBar = alwaysConsumeNavBar;
@@ -148,8 +153,8 @@
* @param src Source to copy insets from
*/
public WindowInsets(WindowInsets src) {
- this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mIsRound, src.mAlwaysConsumeNavBar,
- displayCutoutCopyConstructorArgument(src));
+ this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound,
+ src.mAlwaysConsumeNavBar, displayCutoutCopyConstructorArgument(src));
}
private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) {
@@ -200,7 +205,7 @@
/** @hide */
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
- this(createCompatTypeMap(systemWindowInsets), null, false, false, null);
+ this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null);
}
/**
@@ -225,6 +230,20 @@
typeInsetMap[indexOf(SIDE_BARS)] = Insets.of(insets.left, 0, insets.right, insets.bottom);
}
+ private static boolean[] createCompatVisibilityMap(@Nullable Insets[] typeInsetMap) {
+ boolean[] typeVisibilityMap = new boolean[SIZE];
+ if (typeInsetMap == null) {
+ return typeVisibilityMap;
+ }
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ int index = indexOf(i);
+ if (!Insets.NONE.equals(typeInsetMap[index])) {
+ typeVisibilityMap[index] = true;
+ }
+ }
+ return typeVisibilityMap;
+ }
+
/**
* Used to provide a safe copy of the system window insets to pass through
* to the existing fitSystemWindows method and other similar internals.
@@ -297,6 +316,27 @@
}
/**
+ * Returns whether a set of windows that may cause insets is currently visible on screen,
+ * regardless of whether it actually overlaps with this window.
+ *
+ * @param typeMask Bit mask of {@link InsetType}s to query visibility status.
+ * @return {@code true} if and only if all windows included in {@code typeMask} are currently
+ * visible on screen.
+ * @hide pending unhide
+ */
+ public boolean isVisible(@InsetType int typeMask) {
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ if ((typeMask & i) == 0) {
+ continue;
+ }
+ if (!mTypeVisibilityMap[indexOf(i)]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Returns the left system window inset in pixels.
*
* <p>The system window inset represents the area of a full-screen window that is
@@ -392,6 +432,7 @@
public WindowInsets consumeDisplayCutout() {
return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
+ mTypeVisibilityMap,
mIsRound, mAlwaysConsumeNavBar,
null /* displayCutout */);
}
@@ -437,6 +478,7 @@
@NonNull
public WindowInsets consumeSystemWindowInsets() {
return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
+ mTypeVisibilityMap,
mIsRound, mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(this));
}
@@ -594,7 +636,7 @@
@NonNull
public WindowInsets consumeStableInsets() {
return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null,
- mIsRound, mAlwaysConsumeNavBar,
+ mTypeVisibilityMap, mIsRound, mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(this));
}
@@ -671,6 +713,7 @@
mStableInsetsConsumed
? null
: insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
+ mTypeVisibilityMap,
mIsRound, mAlwaysConsumeNavBar,
mDisplayCutoutConsumed
? null
@@ -692,14 +735,15 @@
&& mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
&& Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
&& Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
+ && Arrays.equals(mTypeVisibilityMap, that.mTypeVisibilityMap)
&& Objects.equals(mDisplayCutout, that.mDisplayCutout);
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
- mIsRound, mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
- mStableInsetsConsumed, mDisplayCutoutConsumed);
+ Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mAlwaysConsumeNavBar,
+ mSystemWindowInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed);
}
@@ -754,6 +798,7 @@
private final Insets[] mTypeInsetsMap;
private final Insets[] mTypeMaxInsetsMap;
+ private final boolean[] mTypeVisibilityMap;
private boolean mSystemInsetsConsumed = true;
private boolean mStableInsetsConsumed = true;
@@ -768,6 +813,7 @@
public Builder() {
mTypeInsetsMap = new Insets[SIZE];
mTypeMaxInsetsMap = new Insets[SIZE];
+ mTypeVisibilityMap = new boolean[SIZE];
}
/**
@@ -778,6 +824,7 @@
public Builder(WindowInsets insets) {
mTypeInsetsMap = insets.mTypeInsetsMap.clone();
mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone();
+ mTypeVisibilityMap = insets.mTypeVisibilityMap.clone();
mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
mStableInsetsConsumed = insets.mStableInsetsConsumed;
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
@@ -862,6 +909,29 @@
}
/**
+ * Sets whether windows that can cause insets are currently visible on screen.
+ *
+ *
+ * @see #isVisible(int)
+ *
+ * @param typeMask The bitmask of {@link InsetType} to set the visibility for.
+ * @param visible Whether to mark the windows as visible or not.
+ *
+ * @return itself
+ * @hide pending unhide
+ */
+ @NonNull
+ public Builder setVisible(@InsetType int typeMask, boolean visible) {
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ if ((typeMask & i) == 0) {
+ continue;
+ }
+ mTypeVisibilityMap[indexOf(i)] = visible;
+ }
+ return this;
+ }
+
+ /**
* Sets the stable insets in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
@@ -916,8 +986,8 @@
@NonNull
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
- mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mIsRound,
- mAlwaysConsumeNavBar, mDisplayCutout);
+ mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
+ mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
}
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 6ed2d80..c425e7b 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -34,8 +34,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import dalvik.system.CloseGuard;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -148,9 +146,6 @@
@Retention(RetentionPolicy.SOURCE)
@interface FlushReason{}
-
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
private final Object mLock = new Object();
/**
@@ -185,7 +180,6 @@
@VisibleForTesting
public ContentCaptureSession(@NonNull String id) {
mId = Preconditions.checkNotNull(id);
- mCloseGuard.open("destroy");
}
/** @hide */
@@ -251,8 +245,6 @@
}
mDestroyed = true;
- mCloseGuard.close();
-
// TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
// id) and send it to the cache of batched commands
if (VERBOSE) {
@@ -288,18 +280,6 @@
destroy();
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
- destroy();
- } finally {
- super.finalize();
- }
- }
-
/**
* Notifies the Content Capture Service that a node has been added to the view structure.
*
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index d09323d..112653a 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -219,7 +219,7 @@
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 77cb4cd..fdc34b3 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -103,10 +103,9 @@
final String modelName = String.format(
Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
final int hash = Objects.hash(
- messages.stream()
- .map(ConversationActions.Message::getText)
- .collect(Collectors.toList()),
- context.getPackageName());
+ messages.stream().mapToInt(ActionsSuggestionsHelper::hashMessage),
+ context.getPackageName(),
+ System.currentTimeMillis());
return SelectionSessionLogger.SignatureParser.createSignature(
SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
}
@@ -128,4 +127,8 @@
return result;
}
}
+
+ private static int hashMessage(ConversationActions.Message message) {
+ return Objects.hash(message.getAuthor(), message.getText(), message.getReferenceTime());
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index ed86206..10c7ade 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -73,9 +73,16 @@
/**
* Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}.
* If this is null, this method returns a default text classifier (i.e. either the system text
- * classifier if one exists, or a local text classifier running in this app.)
+ * classifier if one exists, or a local text classifier running in this process.)
+ * <p>
+ * Note that if system textclassifier is in use, requests will be sent to a textclassifier
+ * package provided from OEM. If you want to make sure the requests are handled in your own
+ * process, you should consider {@link #getLocalTextClassifier()} instead. However, the local
+ * textclassifier may return inferior results to those returned by the system
+ * textclassifier.
*
* @see #setTextClassifier(TextClassifier)
+ * @see #getLocalTextClassifier()
*/
@NonNull
public TextClassifier getTextClassifier() {
@@ -215,7 +222,13 @@
return TextClassifier.NO_OP;
}
- private TextClassifier getLocalTextClassifier() {
+ /**
+ * Returns a local textclassifier, which is running in this process.
+ *
+ * @see #getTextClassifier()
+ */
+ @NonNull
+ public TextClassifier getLocalTextClassifier() {
synchronized (mLock) {
if (mLocalTextClassifier == null) {
if (getSettings().isLocalTextClassifierEnabled()) {
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index b84f6f0..cd13cc0 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -72,7 +72,7 @@
TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION,
TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION,
TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL,
- TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY})
+ TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED})
public @interface Type {
// For custom event types, use range 1,000,000+.
}
@@ -121,7 +121,7 @@
@Category private final int mEventCategory;
@Type private final int mEventType;
- @Nullable private final String mEntityType;
+ @Nullable private final String[] mEntityTypes;
@Nullable private final TextClassificationContext mEventContext;
@Nullable private final String mResultId;
private final int mEventIndex;
@@ -139,11 +139,12 @@
// Language detection.
@Nullable private final String mLanguage;
+ private final float mScore;
private TextClassifierEvent(
int eventCategory,
int eventType,
- String entityType,
+ String[] entityTypes,
TextClassificationContext eventContext,
String resultId,
int eventIndex,
@@ -154,10 +155,11 @@
int relativeSuggestedWordStartIndex,
int relativeSuggestedWordEndIndex,
int[] actionIndex,
- String language) {
+ String language,
+ float score) {
mEventCategory = eventCategory;
mEventType = eventType;
- mEntityType = entityType;
+ mEntityTypes = entityTypes;
mEventContext = eventContext;
mResultId = resultId;
mEventIndex = eventIndex;
@@ -169,6 +171,7 @@
mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
mActionIndices = actionIndex;
mLanguage = language;
+ mScore = score;
}
@Override
@@ -180,7 +183,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mEventCategory);
dest.writeInt(mEventType);
- dest.writeString(mEntityType);
+ dest.writeStringArray(mEntityTypes);
dest.writeParcelable(mEventContext, flags);
dest.writeString(mResultId);
dest.writeInt(mEventIndex);
@@ -192,13 +195,14 @@
dest.writeInt(mRelativeSuggestedWordEndIndex);
dest.writeIntArray(mActionIndices);
dest.writeString(mLanguage);
+ dest.writeFloat(mScore);
}
private static TextClassifierEvent readFromParcel(Parcel in) {
return new TextClassifierEvent(
/* eventCategory= */ in.readInt(),
/* eventType= */ in.readInt(),
- /* entityType= */ in.readString(),
+ /* entityTypes=*/ in.readStringArray(),
/* eventContext= */ in.readParcelable(null),
/* resultId= */ in.readString(),
/* eventIndex= */ in.readInt(),
@@ -209,7 +213,8 @@
/* relativeSuggestedWordStartIndex= */ in.readInt(),
/* relativeSuggestedWordEndIndex= */ in.readInt(),
/* actionIndices= */ in.createIntArray(),
- /* language= */ in.readString());
+ /* language= */ in.readString(),
+ /* score= */ in.readFloat());
}
/**
@@ -229,11 +234,11 @@
}
/**
- * Returns the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ * Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
*/
- @Nullable
- public String getEntityType() {
- return mEntityType;
+ @NonNull
+ public String[] getEntityTypes() {
+ return mEntityTypes;
}
/**
@@ -327,13 +332,20 @@
}
/**
+ * Returns the score of the suggestion.
+ */
+ public float getScore() {
+ return mScore;
+ }
+
+ /**
* Builder to build a text classifier event.
*/
public static final class Builder {
private final int mEventCategory;
private final int mEventType;
- @Nullable private String mEntityType;
+ private String[] mEntityTypes = new String[0];
@Nullable private TextClassificationContext mEventContext;
@Nullable private String mResultId;
private int mEventIndex;
@@ -345,6 +357,7 @@
private int mRelativeSuggestedWordEndIndex;
private int[] mActionIndices = new int[0];
@Nullable private String mLanguage;
+ private float mScore;
/**
* Creates a builder for building {@link TextClassifierEvent}s.
@@ -358,11 +371,12 @@
}
/**
- * Sets the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
+ * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}.
*/
@NonNull
- public Builder setEntityType(@Nullable String entityType) {
- mEntityType = entityType;
+ public Builder setEntityTypes(@NonNull String... entityTypes) {
+ mEntityTypes = new String[entityTypes.length];
+ System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length);
return this;
}
@@ -478,6 +492,15 @@
}
/**
+ * Sets the score of the suggestion.
+ */
+ @NonNull
+ public Builder setScore(float score) {
+ mScore = score;
+ return this;
+ }
+
+ /**
* Builds and returns a text classifier event.
*/
@NonNull
@@ -486,7 +509,7 @@
return new TextClassifierEvent(
mEventCategory,
mEventType,
- mEntityType,
+ mEntityTypes,
mEventContext,
mResultId,
mEventIndex,
@@ -497,7 +520,8 @@
mRelativeSuggestedWordStartIndex,
mRelativeSuggestedWordEndIndex,
mActionIndices,
- mLanguage);
+ mLanguage,
+ mScore);
}
// TODO: Add build(boolean validate).
}
@@ -507,7 +531,7 @@
StringBuilder out = new StringBuilder(128);
out.append("TextClassifierEvent{");
out.append("mEventCategory=").append(mEventCategory);
- out.append(", mEventType=").append(mEventType);
+ out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes));
out.append(", mEventContext=").append(mEventContext);
out.append(", mResultId=").append(mResultId);
out.append(", mEventIndex=").append(mEventIndex);
@@ -519,6 +543,7 @@
out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex);
out.append(", mActionIndices=").append(Arrays.toString(mActionIndices));
out.append(", mLanguage=").append(mLanguage);
+ out.append(", mScore=").append(mScore);
out.append("}");
return out.toString();
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
index 439e594..5563dfc 100644
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -15,12 +15,15 @@
*/
package android.view.textclassifier;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_SESSION_ID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;
import android.metrics.LogMaker;
@@ -60,16 +63,30 @@
return;
}
final LogMaker log = new LogMaker(category)
- .setType(getLogType(event))
- .addTaggedData(FIELD_SELECTION_SESSION_ID, event.getResultId())
+ .setSubtype(getLogType(event))
+ .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
.addTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME, event.getEventTime())
.addTaggedData(FIELD_TEXTCLASSIFIER_MODEL,
SelectionSessionLogger.SignatureParser.getModelName(event.getResultId()))
- .addTaggedData(FIELD_SELECTION_ENTITY_TYPE, event.getEntityType());
+ .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore());
+
+ String[] entityTypes = event.getEntityTypes();
+ // TRON does not support a field of list type, and thus workaround by store them
+ // in three separate fields. This is no longer an issue once we have moved to Westworld.
+ if (entityTypes.length >= 1) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
+ }
+ if (entityTypes.length >= 2) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
+ }
+ if (entityTypes.length >= 3) {
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
+ }
TextClassificationContext eventContext = event.getEventContext();
if (eventContext != null) {
- log.addTaggedData(FIELD_SELECTION_WIDGET_TYPE, eventContext.getWidgetType());
- log.addTaggedData(FIELD_SELECTION_WIDGET_VERSION, eventContext.getWidgetVersion());
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
+ log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
+ eventContext.getWidgetVersion());
log.setPackageName(eventContext.getPackageName());
}
mMetricsLogger.write(log);
@@ -94,6 +111,8 @@
return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
case TextClassifierEvent.TYPE_MANUAL_REPLY:
return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
+ case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
+ return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
default:
return MetricsEvent.VIEW_UNKNOWN;
}
@@ -127,14 +146,22 @@
if (!Log.ENABLE_FULL_LOGGING) {
return;
}
- final String id = String.valueOf(log.getTaggedData(FIELD_SELECTION_SESSION_ID));
+ final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
final String categoryName = toCategoryName(log.getCategory());
- final String eventName = toEventName(log.getType());
- final String widgetType = String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_TYPE));
+ final String eventName = toEventName(log.getSubtype());
+ final String widgetType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
final String widgetVersion =
- String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_VERSION));
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
- final String entityType = String.valueOf(log.getTaggedData(FIELD_SELECTION_ENTITY_TYPE));
+ final String firstEntityType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
+ final String secondEntityType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
+ final String thirdEntityType =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
+ final String score =
+ String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));
StringBuilder builder = new StringBuilder();
builder.append("writeEvent: ");
@@ -144,7 +171,10 @@
builder.append(", widgetType=").append(widgetType);
builder.append(", widgetVersion=").append(widgetVersion);
builder.append(", model=").append(model);
- builder.append(", entityType=").append(entityType);
+ builder.append(", firstEntityType=").append(firstEntityType);
+ builder.append(", secondEntityType=").append(secondEntityType);
+ builder.append(", thirdEntityType=").append(thirdEntityType);
+ builder.append(", score=").append(score);
Log.v(TAG, builder.toString());
}
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 7c371cb..d0102a7 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,13 +17,10 @@
package com.android.internal.app;
import android.annotation.NonNull;
-import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -31,8 +28,6 @@
import android.provider.Settings;
import android.util.Log;
-import com.android.internal.R;
-
import java.util.ArrayList;
import java.util.Set;
@@ -44,14 +39,6 @@
private static final String TAG = "AssistUtils";
- /**
- * Sentinel value for "no default assistant specified."
- *
- * Empty string is already used to represent an explicit setting of No Assistant. null cannot
- * be used because we can't represent a null value in XML.
- */
- private static final String UNSET = "#+UNSET";
-
private final Context mContext;
private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
@@ -186,37 +173,9 @@
Settings.Secure.ASSISTANT, userId);
if (setting != null) {
return ComponentName.unflattenFromString(setting);
- }
-
- final String defaultSetting = mContext.getResources().getString(
- R.string.config_defaultAssistantComponentName);
- if (defaultSetting != null && !defaultSetting.equals(UNSET)) {
- return ComponentName.unflattenFromString(defaultSetting);
- }
-
- // Fallback to keep backward compatible behavior when there is no user setting.
- if (activeServiceSupportsAssistGesture()) {
- return getActiveServiceComponentName();
- }
-
- if (UNSET.equals(defaultSetting)) {
+ } else {
return null;
}
-
- final SearchManager searchManager =
- (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
- if (searchManager == null) {
- return null;
- }
- final Intent intent = searchManager.getAssistIntent(false);
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo info = pm.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY,
- userId);
- if (info != null) {
- return new ComponentName(info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
- }
- return null;
}
public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 30137e38..42acb09 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -513,6 +513,7 @@
void queryTargetServices(ChooserListAdapter adapter) {
final PackageManager pm = getPackageManager();
+ ShortcutManager sm = (ShortcutManager) getSystemService(ShortcutManager.class);
int targetsToQuery = 0;
for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
@@ -522,6 +523,11 @@
continue;
}
final ActivityInfo ai = dri.getResolveInfo().activityInfo;
+ if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS
+ && sm.hasShareTargets(ai.packageName)) {
+ // Share targets will be queried from ShortcutManager
+ continue;
+ }
final Bundle md = ai.metaData;
final String serviceName = md != null ? convertServiceName(ai.packageName,
md.getString(ChooserTargetService.META_DATA_NAME)) : null;
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index b03fde7..2ac0e4d 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -16,7 +16,6 @@
package com.android.internal.app;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -24,18 +23,13 @@
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.ColorDisplayManager.AutoMode;
+import android.hardware.display.ColorDisplayManager.ColorMode;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
-import android.os.SystemProperties;
import android.provider.Settings.Secure;
-import android.provider.Settings.System;
import android.util.Slog;
-import com.android.internal.R;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.time.LocalTime;
/**
@@ -49,35 +43,6 @@
private static final String TAG = "ColorDisplayController";
private static final boolean DEBUG = false;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED, COLOR_MODE_AUTOMATIC})
- public @interface ColorMode {}
-
- /**
- * Color mode with natural colors.
- *
- * @see #setColorMode(int)
- */
- public static final int COLOR_MODE_NATURAL = 0;
- /**
- * Color mode with boosted colors.
- *
- * @see #setColorMode(int)
- */
- public static final int COLOR_MODE_BOOSTED = 1;
- /**
- * Color mode with saturated colors.
- *
- * @see #setColorMode(int)
- */
- public static final int COLOR_MODE_SATURATED = 2;
- /**
- * Color mode with automatic colors.
- *
- * @see #setColorMode(int)
- */
- public static final int COLOR_MODE_AUTOMATIC = 3;
-
private final Context mContext;
private final int mUserId;
private final ColorDisplayManager mColorDisplayManager;
@@ -197,74 +162,10 @@
}
/**
- * Get the current color mode from system properties, or return -1.
- *
- * See com.android.server.display.DisplayTransformManager.
- */
- private @ColorMode int getCurrentColorModeFromSystemProperties() {
- final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
- if (displayColorSetting == 0) {
- return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
- ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
- } else if (displayColorSetting == 1) {
- return COLOR_MODE_SATURATED;
- } else if (displayColorSetting == 2) {
- return COLOR_MODE_AUTOMATIC;
- } else {
- return -1;
- }
- }
-
- private boolean isColorModeAvailable(@ColorMode int colorMode) {
- final int[] availableColorModes = mContext.getResources().getIntArray(
- R.array.config_availableColorModes);
- if (availableColorModes != null) {
- for (int mode : availableColorModes) {
- if (mode == colorMode) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
* Get the current color mode.
*/
public int getColorMode() {
- if (getAccessibilityTransformActivated()) {
- if (isColorModeAvailable(COLOR_MODE_SATURATED)) {
- return COLOR_MODE_SATURATED;
- } else if (isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
- return COLOR_MODE_AUTOMATIC;
- }
- }
-
- int colorMode = System.getIntForUser(mContext.getContentResolver(),
- System.DISPLAY_COLOR_MODE, -1, mUserId);
- if (colorMode == -1) {
- // There might be a system property controlling color mode that we need to respect; if
- // not, this will set a suitable default.
- colorMode = getCurrentColorModeFromSystemProperties();
- }
-
- // This happens when a color mode is no longer available (e.g., after system update or B&R)
- // or the device does not support any color mode.
- if (!isColorModeAvailable(colorMode)) {
- if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
- colorMode = COLOR_MODE_NATURAL;
- } else if (colorMode == COLOR_MODE_SATURATED
- && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
- colorMode = COLOR_MODE_AUTOMATIC;
- } else if (colorMode == COLOR_MODE_AUTOMATIC
- && isColorModeAvailable(COLOR_MODE_SATURATED)) {
- colorMode = COLOR_MODE_SATURATED;
- } else {
- colorMode = -1;
- }
- }
-
- return colorMode;
+ return mColorDisplayManager.getColorMode();
}
/**
@@ -273,11 +174,7 @@
* @param colorMode the color mode
*/
public void setColorMode(@ColorMode int colorMode) {
- if (!isColorModeAvailable(colorMode)) {
- throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
- }
- System.putIntForUser(mContext.getContentResolver(), System.DISPLAY_COLOR_MODE, colorMode,
- mUserId);
+ mColorDisplayManager.setColorMode(colorMode);
}
/**
@@ -294,18 +191,6 @@
return ColorDisplayManager.getMaximumColorTemperature(mContext);
}
- /**
- * Returns true if any Accessibility color transforms are enabled.
- */
- public boolean getAccessibilityTransformActivated() {
- final ContentResolver cr = mContext.getContentResolver();
- return
- Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
- 0, mUserId) == 1
- || Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
- 0, mUserId) == 1;
- }
-
private void onSettingChanged(@NonNull String setting) {
if (DEBUG) {
Slog.d(TAG, "onSettingChanged: " + setting);
@@ -328,13 +213,6 @@
case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
mCallback.onColorTemperatureChanged(getColorTemperature());
break;
- case System.DISPLAY_COLOR_MODE:
- mCallback.onDisplayColorModeChanged(getColorMode());
- break;
- case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
- case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
- mCallback.onAccessibilityTransformChanged(getAccessibilityTransformActivated());
- break;
}
}
}
@@ -377,14 +255,6 @@
false /* notifyForDescendants */, mContentObserver, mUserId);
cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
false /* notifyForDescendants */, mContentObserver, mUserId);
- cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
- false /* notifyForDecendants */, mContentObserver, mUserId);
- cr.registerContentObserver(
- Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
- false /* notifyForDecendants */, mContentObserver, mUserId);
- cr.registerContentObserver(
- Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
- false /* notifyForDecendants */, mContentObserver, mUserId);
}
}
}
@@ -424,19 +294,5 @@
* @param colorTemperature the color temperature to tint the screen
*/
default void onColorTemperatureChanged(int colorTemperature) {}
-
- /**
- * Callback invoked when the color mode changes.
- *
- * @param displayColorMode the color mode
- */
- default void onDisplayColorModeChanged(int displayColorMode) {}
-
- /**
- * Callback invoked when Accessibility color transforms change.
- *
- * @param state the state Accessibility color transforms (true of active)
- */
- default void onAccessibilityTransformChanged(boolean state) {}
}
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 7600dc9..8978496 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -100,6 +100,7 @@
* @param backDisposition disposition flags
* @see android.inputmethodservice.InputMethodService#IME_ACTIVE
* @see android.inputmethodservice.InputMethodService#IME_VISIBLE
+ * @see android.inputmethodservice.InputMethodService#IME_INVISIBLE
* @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT
* @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING
*/
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 9bacf9b..f848346 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -64,6 +64,9 @@
private boolean mUseBpfStats;
+ // A persistent Snapshot since device start for eBPF stats
+ private final NetworkStats mPersistSnapshot;
+
// TODO: only do adjustments in NetworkStatsService and remove this.
/**
* (Stacked interface) -> (base interface) association for all connected ifaces since boot.
@@ -135,6 +138,7 @@
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
+ mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -268,6 +272,7 @@
return stats;
}
+ // TODO: delete the lastStats parameter
private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces,
int limitTag, NetworkStats lastStats) throws IOException {
if (USE_NATIVE_PARSING) {
@@ -278,16 +283,28 @@
} else {
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
- limitIfaces, limitTag, mUseBpfStats) != 0) {
- throw new IOException("Failed to parse network stats");
+ if (mUseBpfStats) {
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
+ null, TAG_ALL, mUseBpfStats) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
+ mPersistSnapshot.combineAllValues(stats);
+ NetworkStats result = mPersistSnapshot.clone();
+ result.filter(limitUid, limitIfaces, limitTag);
+ return result;
+ } else {
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
+ limitIfaces, limitTag, mUseBpfStats) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ if (SANITY_CHECK_NATIVE) {
+ final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
+ limitIfaces, limitTag);
+ assertEquals(javaStats, stats);
+ }
+ return stats;
}
- if (SANITY_CHECK_NATIVE) {
- final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
- limitIfaces, limitTag);
- assertEquals(javaStats, stats);
- }
- return stats;
} else {
return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag);
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 97d5a65..2ee902a 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -40,7 +40,7 @@
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext, int missingMethods,
- in EditorInfo attribute, boolean restarting);
+ in EditorInfo attribute, boolean restarting, boolean preRenderImeViews);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d79eb94..f06165c 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -451,6 +451,8 @@
optional SettingProto gup_blacklist = 11;
// List of Apps that are allowed to use Game Driver package.
optional SettingProto game_driver_whitelist = 12;
+ // ANGLE - List of Apps that can check ANGLE rules
+ optional SettingProto angle_whitelist = 13;
}
optional Gpu gpu = 59;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0c0b4d7..4a54bd7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3528,12 +3528,6 @@
android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
- <!-- Allows an application to access and modify always-on VPN configuration.
- <p>Not for use by third-party or privileged applications.
- @hide -->
- <permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN"
- android:protectionLevel="signature" />
-
<!-- Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d93cd82..49f2c84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3729,9 +3729,6 @@
<!-- Whether or not the "SMS app service" feature is enabled -->
<bool name="config_useSmsAppService">true</bool>
- <!-- Component name for default assistant on this device -->
- <string name="config_defaultAssistantComponentName">#+UNSET</string>
-
<!-- Class name for the InputEvent compatibility processor override.
Empty string means use the default compatibility processor
(android.view.InputEventCompatProcessor). -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4235341..ec1bac1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2988,7 +2988,7 @@
</public-group>
<public-group type="array" first-id="0x01070006">
- <!-- @hide @SystemApi -->
+ <!-- @hide @TestApi @SystemApi -->
<public name="config_defaultRoleHolders" />
</public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cbb4cb2..d556130 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3528,8 +3528,6 @@
<java-symbol type="bool" name="config_useSmsAppService" />
- <java-symbol type="string" name="config_defaultAssistantComponentName" />
-
<java-symbol type="id" name="transition_overlay_view_tag" />
<java-symbol type="dimen" name="rounded_corner_radius" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 318f2ea..a15dbc8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -390,9 +390,8 @@
Settings.Global.POWER_MANAGER_CONSTANTS,
Settings.Global.PREFERRED_NETWORK_MODE,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED,
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
Settings.Global.RADIO_BLUETOOTH,
@@ -482,6 +481,7 @@
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST,
Settings.Global.GUP_DEV_ALL_APPS,
Settings.Global.GUP_DEV_OPT_IN_APPS,
Settings.Global.GUP_DEV_OPT_OUT_APPS,
@@ -574,7 +574,6 @@
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
Settings.Secure.ALWAYS_ON_VPN_APP,
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
- Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST,
Settings.Secure.ANDROID_ID,
Settings.Secure.ANR_SHOW_BACKGROUND,
Settings.Secure.ASSISTANT,
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index d57fa8f..6a83c29b 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -20,6 +20,7 @@
import static android.view.WindowInsets.Type.sideBars;
import static android.view.WindowInsets.Type.topBar;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.graphics.Insets;
@@ -73,4 +74,29 @@
assertEquals(Insets.of(0, 50, 0, 0), insets.getInsets(topBar()));
assertEquals(Insets.of(0, 0, 30, 10), insets.getInsets(sideBars()));
}
+
+ // TODO: Move this to CTS once API made public
+ @Test
+ public void visibility() {
+ Builder b = new WindowInsets.Builder();
+ b.setInsets(sideBars(), Insets.of(0, 0, 0, 100));
+ b.setInsets(ime(), Insets.of(0, 0, 0, 300));
+ b.setVisible(sideBars(), true);
+ b.setVisible(ime(), true);
+ WindowInsets insets = b.build();
+ assertTrue(insets.isVisible(sideBars()));
+ assertTrue(insets.isVisible(sideBars() | ime()));
+ assertFalse(insets.isVisible(sideBars() | topBar()));
+ }
+
+ // TODO: Move this to CTS once API made public
+ @Test
+ public void consume_doesntChangeVisibility() {
+ Builder b = new WindowInsets.Builder();
+ b.setInsets(ime(), Insets.of(0, 0, 0, 300));
+ b.setVisible(ime(), true);
+ WindowInsets insets = b.build();
+ insets = insets.consumeSystemWindowInsets();
+ assertTrue(insets.isVisible(ime()));
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
index b1b7416..73af567 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -18,9 +18,10 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
import static com.google.common.truth.Truth.assertThat;
@@ -71,7 +72,8 @@
new TextClassifierEvent.Builder(
TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
TextClassifierEvent.TYPE_SMART_ACTION)
- .setEntityType(ConversationAction.TYPE_CALL_PHONE)
+ .setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
+ .setScore(0.5f)
.setEventTime(EVENT_TIME)
.setEventContext(textClassificationContext)
.build();
@@ -83,15 +85,18 @@
LogMaker logMaker = captor.getValue();
assertThat(logMaker.getCategory()).isEqualTo(
CONVERSATION_ACTIONS);
- assertThat(logMaker.getType()).isEqualTo(
+ assertThat(logMaker.getSubtype()).isEqualTo(
ACTION_TEXT_SELECTION_SMART_SHARE);
- assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
+ assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
.isEqualTo(ConversationAction.TYPE_CALL_PHONE);
+ assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
+ .isWithin(0.00001f).of(0.5f);
assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
.isEqualTo(EVENT_TIME);
assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
- assertThat(logMaker.getTaggedData(FIELD_SELECTION_WIDGET_TYPE))
+ assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE))
.isEqualTo(WIDGET_TYPE);
+
}
@Test
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 1661905..8508274 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -31,8 +31,8 @@
// Cache size limits.
static const size_t maxKeySize = 1024;
-static const size_t maxValueSize = 64 * 1024;
-static const size_t maxTotalSize = 512 * 1024;
+static const size_t maxValueSize = 512 * 1024;
+static const size_t maxTotalSize = 1024 * 1024;
ShaderCache::ShaderCache() {
// There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 9b6ad38..9064ebe 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -81,5 +81,5 @@
"com.android.keyguard",
],
- annotation_processors: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler-2.19"],
}
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 052566d..e591ea9 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -65,8 +65,9 @@
<com.android.systemui.statusbar.car.CarFacetButton
android:id="@+id/music_nav"
style="@style/NavigationBarButton"
+ systemui:categories="android.intent.category.APP_MUSIC"
systemui:icon="@drawable/car_ic_music"
- systemui:intent="intent:#Intent;component=com.android.car.media/.MediaActivity;launchFlags=0x14000000;end"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end"
systemui:packages="com.android.car.media"
systemui:selectedIcon="@drawable/car_ic_music_selected"
systemui:useMoreIcon="false"
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 dbddf71..b37c5e6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -114,9 +114,16 @@
new DeviceProvisionedController.DeviceProvisionedListener() {
@Override
public void onDeviceProvisionedChanged() {
- mDeviceIsProvisioned =
- mDeviceProvisionedController.isDeviceProvisioned();
- restartNavBars();
+ mHandler.post(() -> {
+ // on initial boot we are getting a call even though the value
+ // is the same so we are confirming the reset is needed
+ boolean deviceProvisioned =
+ mDeviceProvisionedController.isDeviceProvisioned();
+ if (mDeviceIsProvisioned != deviceProvisioned) {
+ mDeviceIsProvisioned = deviceProvisioned;
+ restartNavBars();
+ }
+ });
}
});
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 0d528e7..bc6e2fc 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -202,7 +202,7 @@
}
TextClassifierEvent textClassifierEvent =
createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
- .setEntityType(ConversationAction.TYPE_TEXT_REPLY)
+ .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
.build();
mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
@@ -225,7 +225,7 @@
}
TextClassifierEvent textClassifierEvent =
createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
- .setEntityType(actionType)
+ .setEntityTypes(actionType)
.build();
mTextClassifier.onTextClassifierEvent(textClassifierEvent);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 7357fe6..42afb69 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
+import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -123,4 +124,12 @@
return null;
}
+ /**
+ * Returns a boolean indicating whether the given package is a hidden system module
+ */
+ public static boolean isHiddenSystemModule(Context context, String packageName) {
+ return ApplicationsState.getInstance((Application) context.getApplicationContext())
+ .isHiddenModule(packageName);
+ }
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index a936df2..c9fbc7b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -29,6 +29,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
@@ -71,6 +72,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -95,9 +97,14 @@
static ApplicationsState sInstance;
public static ApplicationsState getInstance(Application app) {
+ return getInstance(app, AppGlobals.getPackageManager());
+ }
+
+ @VisibleForTesting
+ static ApplicationsState getInstance(Application app, IPackageManager iPackageManager) {
synchronized (sLock) {
if (sInstance == null) {
- sInstance = new ApplicationsState(app);
+ sInstance = new ApplicationsState(app, iPackageManager);
}
return sInstance;
}
@@ -132,6 +139,7 @@
String mCurComputingSizePkg;
int mCurComputingSizeUserId;
boolean mSessionsChanged;
+ final HashSet<String> mHiddenModules = new HashSet<>();
// Temporary for dispatching session callbacks. Only touched by main thread.
final ArrayList<WeakReference<Session>> mActiveSessions = new ArrayList<>();
@@ -172,11 +180,11 @@
FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
- private ApplicationsState(Application app) {
+ private ApplicationsState(Application app, IPackageManager iPackageManager) {
mContext = app;
mPm = mContext.getPackageManager();
mDrawableFactory = IconDrawableFactory.newInstance(mContext);
- mIpm = AppGlobals.getPackageManager();
+ mIpm = iPackageManager;
mUm = mContext.getSystemService(UserManager.class);
mStats = mContext.getSystemService(StorageStatsManager.class);
for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
@@ -194,6 +202,13 @@
mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */);
+ for (ModuleInfo info : moduleInfos) {
+ if (info.isHidden()) {
+ mHiddenModules.add(info.getPackageName());
+ }
+ }
+
/**
* This is a trick to prevent the foreground thread from being delayed.
* The problem is that Dalvik monitors are initially spin locks, to keep
@@ -283,6 +298,10 @@
}
mHaveDisabledApps = true;
}
+ if (isHiddenModule(info.packageName)) {
+ mApplications.remove(i--);
+ continue;
+ }
if (!mHaveInstantApps && AppUtils.isInstant(info)) {
mHaveInstantApps = true;
}
@@ -314,10 +333,15 @@
public boolean haveDisabledApps() {
return mHaveDisabledApps;
}
+
public boolean haveInstantApps() {
return mHaveInstantApps;
}
+ boolean isHiddenModule(String packageName) {
+ return mHiddenModules.contains(packageName);
+ }
+
void doPauseIfNeededLocked() {
if (!mResumed) {
return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index d957801..305a1ff 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -22,7 +22,6 @@
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
@@ -35,14 +34,14 @@
import android.text.format.DateUtils;
import android.util.Pair;
+import androidx.annotation.VisibleForTesting;
+import androidx.loader.content.AsyncTaskLoader;
+
import com.android.settingslib.NetworkPolicyEditor;
import java.time.ZonedDateTime;
import java.util.Iterator;
-import androidx.annotation.VisibleForTesting;
-import androidx.loader.content.AsyncTaskLoader;
-
/**
* Loader for network data usage history. It returns a list of usage data per billing cycle.
*/
@@ -121,8 +120,7 @@
long cycleEnd = historyEnd;
while (cycleEnd > historyStart) {
- final long cycleStart = Math.max(
- historyStart, cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4));
+ final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
recordUsage(cycleStart, cycleEnd);
cycleEnd = cycleStart;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index ccec175a..a098ecc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -18,7 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.shadow.api.Shadow.extract;
@@ -33,12 +35,16 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.IconDrawableFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -46,11 +52,11 @@
import com.android.settingslib.applications.ApplicationsState.Session;
import com.android.settingslib.testutils.shadow.ShadowUserManager;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -78,6 +84,8 @@
/** Class under test */
private ApplicationsState mApplicationsState;
+ private Session mSession;
+
@Mock
private Callbacks mCallbacks;
@@ -85,6 +93,8 @@
private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
@Mock
private StorageStatsManager mStorageStatsManager;
+ @Mock
+ private IPackageManager mPackageManagerService;
@Implements(value = IconDrawableFactory.class)
public static class ShadowIconDrawableFactory {
@@ -99,6 +109,11 @@
public static class ShadowPackageManager extends
org.robolectric.shadows.ShadowApplicationPackageManager {
+ // test installed modules, 2 regular, 2 hidden
+ private final String[] mModuleNames = {
+ "test.module.1", "test.hidden.module.2", "test.hidden.module.3", "test.module.4"};
+ private final List<ModuleInfo> mInstalledModules = new ArrayList<>();
+
@Implementation
protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
ResolveInfo resolveInfo = new ResolveInfo();
@@ -109,6 +124,16 @@
return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
}
+ @Implementation
+ public List<ModuleInfo> getInstalledModules(int flags) {
+ if (mInstalledModules.isEmpty()) {
+ for (String moduleName : mModuleNames) {
+ mInstalledModules.add(createModuleInfo(moduleName));
+ }
+ }
+ return mInstalledModules;
+ }
+
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
@PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
List<ResolveInfo> resolveInfos = new ArrayList<>();
@@ -121,6 +146,15 @@
resolveInfos.add(resolveInfo);
return resolveInfos;
}
+
+ private ModuleInfo createModuleInfo(String packageName) {
+ final ModuleInfo info = new ModuleInfo();
+ info.setName(packageName);
+ info.setPackageName(packageName);
+ // will treat any app with package name that contains "hidden" as hidden module
+ info.setHidden(!TextUtils.isEmpty(packageName) && packageName.contains("hidden"));
+ return info;
+ }
}
@Before
@@ -136,12 +170,28 @@
storageStats.codeBytes = 10;
storageStats.dataBytes = 20;
storageStats.cacheBytes = 30;
- when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
- anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+ when(mStorageStatsManager.queryStatsForPackage(any(UUID.class),
+ anyString(), any(UserHandle.class))).thenReturn(storageStats);
+
+ // Set up 3 installed apps, in which 1 is hidden module
+ final List<ApplicationInfo> infos = new ArrayList<>();
+ infos.add(createApplicationInfo("test.package.1"));
+ infos.add(createApplicationInfo("test.hidden.module.2"));
+ infos.add(createApplicationInfo("test.package.3"));
+ when(mPackageManagerService.getInstalledApplications(
+ anyInt() /* flags */, anyInt() /* userId */)).thenReturn(new ParceledListSlice(infos));
ApplicationsState.sInstance = null;
- mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+ mApplicationsState =
+ ApplicationsState.getInstance(RuntimeEnvironment.application, mPackageManagerService);
mApplicationsState.clearEntries();
+
+ mSession = mApplicationsState.newSession(mCallbacks);
+ }
+
+ @After
+ public void tearDown() {
+ mSession.onDestroy();
}
private ApplicationInfo createApplicationInfo(String packageName) {
@@ -187,12 +237,11 @@
@Test
public void testDefaultSessionLoadsAll() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.onResume();
+ mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
addApp(LAUNCHABLE_PACKAGE_NAME, 2);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -211,17 +260,15 @@
AppEntry launchableEntry = findAppEntry(appEntries, 2);
assertThat(launchableEntry.hasLauncherEntry).isTrue();
assertThat(launchableEntry.launcherEntryEnabled).isTrue();
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsIconsOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+ mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -232,17 +279,15 @@
assertThat(launchableEntry.icon).isNotNull();
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.hasLauncherEntry).isFalse();
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsSizesOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+ mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -253,17 +298,15 @@
assertThat(launchableEntry.icon).isNull();
assertThat(launchableEntry.hasLauncherEntry).isFalse();
assertThat(launchableEntry.size).isGreaterThan(0L);
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsHomeOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+ mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -275,17 +318,15 @@
assertThat(launchableEntry.hasLauncherEntry).isFalse();
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.isHomeApp).isTrue();
- session.onDestroy();
}
@Test
public void testCustomSessionLoadsLeanbackOnly() {
- Session session = mApplicationsState.newSession(mCallbacks);
- session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
- session.onResume();
+ mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+ mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
- session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -298,6 +339,16 @@
assertThat(launchableEntry.isHomeApp).isFalse();
assertThat(launchableEntry.hasLauncherEntry).isTrue();
assertThat(launchableEntry.launcherEntryEnabled).isTrue();
- session.onDestroy();
}
+
+ @Test
+ public void onResume_shouldNotIncludeSystemHiddenModule() {
+ mSession.onResume();
+
+ final List<ApplicationInfo> mApplications = mApplicationsState.mApplications;
+ assertThat(mApplications).hasSize(2);
+ assertThat(mApplications.get(0).packageName).isEqualTo("test.package.1");
+ assertThat(mApplications.get(1).packageName).isEqualTo("test.package.3");
+ }
+
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 2d8ea12..b8a143a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -130,7 +130,8 @@
.thenReturn(networkHistory);
final long now = System.currentTimeMillis();
final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
- when(networkHistory.getStart()).thenReturn(fourWeeksAgo);
+ final long twoDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 2);
+ when(networkHistory.getStart()).thenReturn(twoDaysAgo);
when(networkHistory.getEnd()).thenReturn(now);
mLoader.loadFourWeeksData();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 850a3c2..aff6f04 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -698,6 +698,9 @@
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
dumpSetting(s, p,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST,
+ GlobalSettingsProto.Gpu.ANGLE_WHITELIST);
+ dumpSetting(s, p,
Settings.Global.GPU_DEBUG_LAYER_APP,
GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
dumpSetting(s, p,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8be67d9..0a62b7c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -73,7 +73,7 @@
"com.android.keyguard",
],
- annotation_processors: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler-2.19"],
}
android_library {
@@ -127,7 +127,7 @@
"--extra-packages",
"com.android.keyguard:com.android.systemui",
],
- annotation_processors: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler-2.19"],
}
android_app {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 0b1dab1..fc84332 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -20,14 +20,14 @@
import android.view.View;
import android.view.ViewGroup;
-import java.util.ArrayList;
-
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.annotations.DependsOn;
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+
+import java.util.ArrayList;
@ProvidesInterface(action = NotificationMenuRowPlugin.ACTION,
version = NotificationMenuRowPlugin.VERSION)
@@ -149,6 +149,12 @@
public boolean canBeDismissed();
/**
+ * Informs the menu whether dismiss gestures are left-to-right or right-to-left.
+ */
+ default void setDismissRtl(boolean dismissRtl) {
+ }
+
+ /**
* Determines whether the menu should remain open given its current state, or snap closed.
* @return true if the menu should remain open, false otherwise.
*/
diff --git a/packages/SystemUI/res/layout/qs_footer_carrier.xml b/packages/SystemUI/res/layout/qs_footer_carrier.xml
new file mode 100644
index 0000000..bd492b0
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_footer_carrier.xml
@@ -0,0 +1,49 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/linear_footer_carrier"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ android:gravity="center_vertical|start"
+ android:background="@android:color/transparent"
+ android:clickable="false"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingStart="16dp" >
+
+ <include
+ layout="@layout/mobile_signal_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone" />
+
+ <view class="com.android.systemui.qs.QSFooterImpl$QSCarrierText"
+ android:id="@+id/qs_carrier_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textDirection="locale"
+ android:singleLine="true" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 890bf5d..abf9e05 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -42,30 +42,31 @@
android:gravity="end" >
<LinearLayout
+ android:id="@+id/qs_mobile"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|start"
- android:paddingStart="16dp">
+ android:orientation="horizontal"
+ android:layout_marginEnd="32dp">
<include
- layout="@layout/mobile_signal_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
+ layout="@layout/qs_footer_carrier"
+ android:id="@+id/carrier1" />
+
+ <View
+ android:id="@+id/qs_carrier_divider"
+ android:layout_width="2dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="15dp"
+ android:layout_marginBottom="15dp"
+ android:background="?android:attr/dividerVertical"
android:visibility="gone" />
- <com.android.keyguard.CarrierText
- android:id="@+id/qs_carrier_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginEnd="32dp"
- android:ellipsize="marquee"
- android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
- android:textColor="?android:attr/textColorPrimary"
- android:textDirection="locale"
- android:singleLine="true" />
+ <include
+ layout="@layout/qs_footer_carrier"
+ android:id="@+id/carrier2"
+ android:visibility="gone"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index bd34bea..5e32dd5 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -68,6 +68,7 @@
<item type="id" name="panel_alpha_animator_tag"/>
<item type="id" name="panel_alpha_animator_start_tag"/>
<item type="id" name="panel_alpha_animator_end_tag"/>
+ <item type="id" name="cross_fade_layer_type_changed_tag"/>
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
index b7d5197..adcb7a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
@@ -17,29 +17,14 @@
package com.android.keyguard;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
-import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.method.SingleLineTransformationMethod;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.TextView;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.settingslib.WirelessUtils;
-
-import java.util.List;
import java.util.Locale;
-import java.util.Objects;
public class CarrierText extends TextView {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -47,77 +32,30 @@
private static CharSequence mSeparator;
- private final boolean mIsEmergencyCallCapable;
-
- private boolean mTelephonyCapable;
-
private boolean mShowMissingSim;
private boolean mShowAirplaneMode;
+ private boolean mShouldMarquee;
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private CarrierTextController mCarrierTextController;
- private WifiManager mWifiManager;
+ private CarrierTextController.CarrierTextCallback mCarrierTextCallback =
+ new CarrierTextController.CarrierTextCallback() {
+ @Override
+ public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+ setText(info.carrierText);
+ }
- private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()];
+ @Override
+ public void startedGoingToSleep() {
+ setSelected(false);
+ }
- private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onRefreshCarrierInfo() {
- if (DEBUG) Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
- + Boolean.toString(mTelephonyCapable));
- updateCarrierText();
- }
-
- public void onFinishedGoingToSleep(int why) {
- setSelected(false);
- };
-
- public void onStartedWakingUp() {
- setSelected(true);
- };
-
- @Override
- public void onTelephonyCapable(boolean capable) {
- if (DEBUG) Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
- + Boolean.toString(capable));
- mTelephonyCapable = capable;
- updateCarrierText();
- }
-
- public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
- if (slotId < 0) {
- Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
- + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
- return;
- }
-
- if (DEBUG) Log.d(TAG,"onSimStateChanged: " + getStatusForIccState(simState));
- if (getStatusForIccState(simState) == StatusMode.SimIoError) {
- mSimErrorState[slotId] = true;
- updateCarrierText();
- } else if (mSimErrorState[slotId]) {
- mSimErrorState[slotId] = false;
- updateCarrierText();
- }
- }
- };
-
- /**
- * The status of this lock screen. Primarily used for widgets on LockScreen.
- */
- private static enum StatusMode {
- Normal, // Normal case (sim card present, it's not locked)
- NetworkLocked, // SIM card is 'network locked'.
- SimMissing, // SIM card is missing.
- SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
- SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
- SimLocked, // SIM card is currently locked
- SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
- SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
- SimIoError, // SIM card is faulty
- SimUnknown // SIM card is unknown
- }
+ @Override
+ public void finishedWakingUp() {
+ setSelected(true);
+ }
+ };
public CarrierText(Context context) {
this(context, null);
@@ -125,8 +63,6 @@
public CarrierText(Context context, AttributeSet attrs) {
super(context, attrs);
- mIsEmergencyCallCapable = context.getResources().getBoolean(
- com.android.internal.R.bool.config_voice_capable);
boolean useAllCaps;
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.CarrierText, 0, 0);
@@ -138,132 +74,6 @@
a.recycle();
}
setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
-
- mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- }
-
- /**
- * Checks if there are faulty cards. Adds the text depending on the slot of the card
- * @param text: current carrier text based on the sim state
- * @param noSims: whether a valid sim card is inserted
- * @return text
- */
- private CharSequence updateCarrierTextWithSimIoError(CharSequence text, boolean noSims) {
- final CharSequence carrier = "";
- CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
- IccCardConstants.State.CARD_IO_ERROR, carrier);
- for (int index = 0; index < mSimErrorState.length; index++) {
- if (mSimErrorState[index]) {
- // In the case when no sim cards are detected but a faulty card is inserted
- // overwrite the text and only show "Invalid card"
- if (noSims) {
- return concatenate(carrierTextForSimIOError,
- getContext().getText(com.android.internal.R.string.emergency_calls_only));
- } else if (index == 0) {
- // prepend "Invalid card" when faulty card is inserted in slot 0
- text = concatenate(carrierTextForSimIOError, text);
- } else {
- // concatenate "Invalid card" when faulty card is inserted in slot 1
- text = concatenate(text, carrierTextForSimIOError);
- }
- }
- }
- return text;
- }
-
- protected void updateCarrierText() {
- boolean allSimsMissing = true;
- boolean anySimReadyAndInService = false;
- CharSequence displayText = null;
-
- List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
- final int N = subs.size();
- if (DEBUG) Log.d(TAG, "updateCarrierText(): " + N);
- for (int i = 0; i < N; i++) {
- int subId = subs.get(i).getSubscriptionId();
- State simState = mKeyguardUpdateMonitor.getSimState(subId);
- CharSequence carrierName = subs.get(i).getCarrierName();
- CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
- if (DEBUG) {
- Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
- }
- if (carrierTextForSimState != null) {
- allSimsMissing = false;
- displayText = concatenate(displayText, carrierTextForSimState);
- }
- if (simState == IccCardConstants.State.READY) {
- ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
- if (ss != null && ss.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
- // hack for WFC (IWLAN) not turning off immediately once
- // Wi-Fi is disassociated or disabled
- if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- || (mWifiManager.isWifiEnabled()
- && mWifiManager.getConnectionInfo() != null
- && mWifiManager.getConnectionInfo().getBSSID() != null)) {
- if (DEBUG) {
- Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
- }
- anySimReadyAndInService = true;
- }
- }
- }
- }
- if (allSimsMissing) {
- if (N != 0) {
- // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
- // This depends on mPlmn containing the text "Emergency calls only" when the radio
- // has some connectivity. Otherwise, it should be null or empty and just show
- // "No SIM card"
- // Grab the first subscripton, because they all should contain the emergency text,
- // described above.
- displayText = makeCarrierStringOnEmergencyCapable(
- getMissingSimMessage(), subs.get(0).getCarrierName());
- } else {
- // We don't have a SubscriptionInfo to get the emergency calls only from.
- // Grab it from the old sticky broadcast if possible instead. We can use it
- // here because no subscriptions are active, so we don't have
- // to worry about MSIM clashing.
- CharSequence text =
- getContext().getText(com.android.internal.R.string.emergency_calls_only);
- Intent i = getContext().registerReceiver(null,
- new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
- if (i != null) {
- String spn = "";
- String plmn = "";
- if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
- spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
- }
- if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
- plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
- }
- if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
- if (Objects.equals(plmn, spn)) {
- text = plmn;
- } else {
- text = concatenate(plmn, spn);
- }
- }
- displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text);
- }
- }
-
- displayText = updateCarrierTextWithSimIoError(displayText, allSimsMissing);
- // APM (airplane mode) != no carrier state. There are carrier services
- // (e.g. WFC = Wi-Fi calling) which may operate in APM.
- if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
- displayText = getAirplaneModeMessage();
- }
- setText(displayText);
- }
-
- private String getMissingSimMessage() {
- return mShowMissingSim && mTelephonyCapable
- ? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
- }
-
- private String getAirplaneModeMessage() {
- return mShowAirplaneMode
- ? getContext().getString(R.string.airplane_mode) : "";
}
@Override
@@ -271,36 +81,27 @@
super.onFinishInflate();
mSeparator = getResources().getString(
com.android.internal.R.string.kg_text_message_separator);
- boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
- setSelected(shouldMarquee); // Allow marquee to work.
+ mCarrierTextController = new CarrierTextController(mContext, mSeparator, mShowAirplaneMode,
+ mShowMissingSim);
+ mShouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
+ setSelected(mShouldMarquee); // Allow marquee to work.
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (ConnectivityManager.from(mContext).isNetworkSupported(
- ConnectivityManager.TYPE_MOBILE)) {
- mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- mKeyguardUpdateMonitor.registerCallback(mCallback);
- } else {
- // Don't listen and clear out the text when the device isn't a phone.
- mKeyguardUpdateMonitor = null;
- setText("");
- }
+ mCarrierTextController.setListening(mCarrierTextCallback);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mKeyguardUpdateMonitor != null) {
- mKeyguardUpdateMonitor.removeCallback(mCallback);
- }
+ mCarrierTextController.setListening(null);
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
-
// Only show marquee when visible
if (visibility == VISIBLE) {
setEllipsize(TextUtils.TruncateAt.MARQUEE);
@@ -309,167 +110,6 @@
}
}
- /**
- * Top-level function for creating carrier text. Makes text based on simState, PLMN
- * and SPN as well as device capabilities, such as being emergency call capable.
- *
- * @param simState
- * @param text
- * @param spn
- * @return Carrier text if not in missing state, null otherwise.
- */
- private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
- CharSequence text) {
- CharSequence carrierText = null;
- StatusMode status = getStatusForIccState(simState);
- switch (status) {
- case Normal:
- carrierText = text;
- break;
-
- case SimNotReady:
- // Null is reserved for denoting missing, in this case we have nothing to display.
- carrierText = ""; // nothing to display yet.
- break;
-
- case NetworkLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- mContext.getText(R.string.keyguard_network_locked_message), text);
- break;
-
- case SimMissing:
- carrierText = null;
- break;
-
- case SimPermDisabled:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(
- R.string.keyguard_permanent_disabled_sim_message_short),
- text);
- break;
-
- case SimMissingLocked:
- carrierText = null;
- break;
-
- case SimLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.keyguard_sim_locked_message),
- text);
- break;
-
- case SimPukLocked:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.keyguard_sim_puk_locked_message),
- text);
- break;
- case SimIoError:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.keyguard_sim_error_message_short),
- text);
- break;
- case SimUnknown:
- carrierText = null;
- break;
- }
-
- return carrierText;
- }
-
- /*
- * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
- */
- private CharSequence makeCarrierStringOnEmergencyCapable(
- CharSequence simMessage, CharSequence emergencyCallMessage) {
- if (mIsEmergencyCallCapable) {
- return concatenate(simMessage, emergencyCallMessage);
- }
- return simMessage;
- }
-
- /**
- * Determine the current status of the lock screen given the SIM state and other stuff.
- */
- private StatusMode getStatusForIccState(IccCardConstants.State simState) {
- // Since reading the SIM may take a while, we assume it is present until told otherwise.
- if (simState == null) {
- return StatusMode.Normal;
- }
-
- final boolean missingAndNotProvisioned =
- !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
- && (simState == IccCardConstants.State.ABSENT ||
- simState == IccCardConstants.State.PERM_DISABLED);
-
- // Assume we're NETWORK_LOCKED if not provisioned
- simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
- switch (simState) {
- case ABSENT:
- return StatusMode.SimMissing;
- case NETWORK_LOCKED:
- return StatusMode.SimMissingLocked;
- case NOT_READY:
- return StatusMode.SimNotReady;
- case PIN_REQUIRED:
- return StatusMode.SimLocked;
- case PUK_REQUIRED:
- return StatusMode.SimPukLocked;
- case READY:
- return StatusMode.Normal;
- case PERM_DISABLED:
- return StatusMode.SimPermDisabled;
- case UNKNOWN:
- return StatusMode.SimUnknown;
- case CARD_IO_ERROR:
- return StatusMode.SimIoError;
- }
- return StatusMode.SimUnknown;
- }
-
- private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
- final boolean plmnValid = !TextUtils.isEmpty(plmn);
- final boolean spnValid = !TextUtils.isEmpty(spn);
- if (plmnValid && spnValid) {
- return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
- } else if (plmnValid) {
- return plmn;
- } else if (spnValid) {
- return spn;
- } else {
- return "";
- }
- }
-
- private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
- String plmn, String spn) {
- int carrierHelpTextId = 0;
- StatusMode status = getStatusForIccState(simState);
- switch (status) {
- case NetworkLocked:
- carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
- break;
-
- case SimMissing:
- carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
- break;
-
- case SimPermDisabled:
- carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
- break;
-
- case SimMissingLocked:
- carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
- break;
-
- case Normal:
- case SimLocked:
- case SimPukLocked:
- break;
- }
-
- return mContext.getText(carrierHelpTextId);
- }
-
private class CarrierTextTransformationMethod extends SingleLineTransformationMethod {
private final Locale mLocale;
private final boolean mAllCaps;
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
new file mode 100644
index 0000000..2ce6965
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.settingslib.WirelessUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Controller that generates text including the carrier names and/or the status of all the SIM
+ * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
+ * separated by a given separator {@link CharSequence}.
+ */
+public class CarrierTextController {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "CarrierTextController";
+
+ private final boolean mIsEmergencyCallCapable;
+ private boolean mTelephonyCapable;
+ private boolean mShowMissingSim;
+ private boolean mShowAirplaneMode;
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private WifiManager mWifiManager;
+ private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()];
+ private CarrierTextCallback mCarrierTextCallback;
+ private Context mContext;
+ private CharSequence mSeparator;
+ private WakefulnessLifecycle mWakefulnessLifecycle;
+ private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+ new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedWakingUp() {
+ mCarrierTextCallback.finishedWakingUp();
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ mCarrierTextCallback.startedGoingToSleep();
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshCarrierInfo() {
+ if (DEBUG) {
+ Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
+ + Boolean.toString(mTelephonyCapable));
+ }
+ updateCarrierText();
+ }
+
+ @Override
+ public void onTelephonyCapable(boolean capable) {
+ if (DEBUG) {
+ Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
+ + Boolean.toString(capable));
+ }
+ mTelephonyCapable = capable;
+ updateCarrierText();
+ }
+
+ public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
+ if (slotId < 0) {
+ Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
+ + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState));
+ if (getStatusForIccState(simState) == CarrierTextController.StatusMode.SimIoError) {
+ mSimErrorState[slotId] = true;
+ updateCarrierText();
+ } else if (mSimErrorState[slotId]) {
+ mSimErrorState[slotId] = false;
+ updateCarrierText();
+ }
+ }
+ };
+
+ /**
+ * The status of this lock screen. Primarily used for widgets on LockScreen.
+ */
+ private enum StatusMode {
+ Normal, // Normal case (sim card present, it's not locked)
+ NetworkLocked, // SIM card is 'network locked'.
+ SimMissing, // SIM card is missing.
+ SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
+ SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
+ SimLocked, // SIM card is currently locked
+ SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
+ SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
+ SimIoError, // SIM card is faulty
+ SimUnknown // SIM card is unknown
+ }
+
+ /**
+ * Controller that provides updates on text with carriers names or SIM status.
+ * Used by {@link CarrierText}.
+ *
+ * @param separator Separator between different parts of the text
+ */
+ public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode,
+ boolean showMissingSim) {
+ mContext = context;
+ mIsEmergencyCallCapable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_voice_capable);
+
+ mShowAirplaneMode = showAirplaneMode;
+ mShowMissingSim = showMissingSim;
+
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mSeparator = separator;
+ mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
+ }
+
+ /**
+ * Checks if there are faulty cards. Adds the text depending on the slot of the card
+ *
+ * @param text: current carrier text based on the sim state
+ * @param noSims: whether a valid sim card is inserted
+ * @return text
+ */
+ private CharSequence updateCarrierTextWithSimIoError(CharSequence text, boolean noSims) {
+ final CharSequence carrier = "";
+ CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
+ IccCardConstants.State.CARD_IO_ERROR, carrier);
+ for (int index = 0; index < mSimErrorState.length; index++) {
+ if (mSimErrorState[index]) {
+ // In the case when no sim cards are detected but a faulty card is inserted
+ // overwrite the text and only show "Invalid card"
+ if (noSims) {
+ return concatenate(carrierTextForSimIOError,
+ getContext().getText(
+ com.android.internal.R.string.emergency_calls_only),
+ mSeparator);
+ } else if (index == 0) {
+ // prepend "Invalid card" when faulty card is inserted in slot 0
+ text = concatenate(carrierTextForSimIOError, text, mSeparator);
+ } else {
+ // concatenate "Invalid card" when faulty card is inserted in slot 1
+ text = concatenate(text, carrierTextForSimIOError, mSeparator);
+ }
+ }
+ }
+ return text;
+ }
+
+ /**
+ * Sets the listening status of this controller. If the callback is null, it is set to
+ * not listening
+ *
+ * @param callback Callback to provide text updates
+ */
+ public void setListening(CarrierTextCallback callback) {
+ if (callback != null) {
+ mCarrierTextCallback = callback;
+ if (ConnectivityManager.from(mContext).isNetworkSupported(
+ ConnectivityManager.TYPE_MOBILE)) {
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ mKeyguardUpdateMonitor.registerCallback(mCallback);
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ } else {
+ // Don't listen and clear out the text when the device isn't a phone.
+ mKeyguardUpdateMonitor = null;
+ callback.updateCarrierInfo(new CarrierTextCallbackInfo("", null, false, null));
+ }
+ } else {
+ mCarrierTextCallback = null;
+ if (mKeyguardUpdateMonitor != null) {
+ mKeyguardUpdateMonitor.removeCallback(mCallback);
+ mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+ }
+ }
+ }
+
+ protected void updateCarrierText() {
+ boolean allSimsMissing = true;
+ boolean anySimReadyAndInService = false;
+ CharSequence displayText = null;
+
+ List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+ final int numSubs = subs.size();
+ final int[] subsIds = new int[numSubs];
+ if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
+ for (int i = 0; i < numSubs; i++) {
+ int subId = subs.get(i).getSubscriptionId();
+ subsIds[i] = subId;
+ IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
+ CharSequence carrierName = subs.get(i).getCarrierName();
+ CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
+ if (DEBUG) {
+ Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
+ }
+ if (carrierTextForSimState != null) {
+ allSimsMissing = false;
+ displayText = concatenate(displayText, carrierTextForSimState, mSeparator);
+ }
+ if (simState == IccCardConstants.State.READY) {
+ ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
+ if (ss != null && ss.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+ // hack for WFC (IWLAN) not turning off immediately once
+ // Wi-Fi is disassociated or disabled
+ if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ || (mWifiManager.isWifiEnabled()
+ && mWifiManager.getConnectionInfo() != null
+ && mWifiManager.getConnectionInfo().getBSSID() != null)) {
+ if (DEBUG) {
+ Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
+ }
+ anySimReadyAndInService = true;
+ }
+ }
+ }
+ }
+ if (allSimsMissing) {
+ if (numSubs != 0) {
+ // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
+ // This depends on mPlmn containing the text "Emergency calls only" when the radio
+ // has some connectivity. Otherwise, it should be null or empty and just show
+ // "No SIM card"
+ // Grab the first subscripton, because they all should contain the emergency text,
+ // described above.
+ displayText = makeCarrierStringOnEmergencyCapable(
+ getMissingSimMessage(), subs.get(0).getCarrierName());
+ } else {
+ // We don't have a SubscriptionInfo to get the emergency calls only from.
+ // Grab it from the old sticky broadcast if possible instead. We can use it
+ // here because no subscriptions are active, so we don't have
+ // to worry about MSIM clashing.
+ CharSequence text =
+ getContext().getText(com.android.internal.R.string.emergency_calls_only);
+ Intent i = getContext().registerReceiver(null,
+ new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
+ if (i != null) {
+ String spn = "";
+ String plmn = "";
+ if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
+ spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
+ }
+ if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
+ plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
+ }
+ if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
+ if (Objects.equals(plmn, spn)) {
+ text = plmn;
+ } else {
+ text = concatenate(plmn, spn, mSeparator);
+ }
+ }
+ displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text);
+ }
+ }
+
+ displayText = updateCarrierTextWithSimIoError(displayText, allSimsMissing);
+ // APM (airplane mode) != no carrier state. There are carrier services
+ // (e.g. WFC = Wi-Fi calling) which may operate in APM.
+ if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
+ displayText = getAirplaneModeMessage();
+ }
+
+ if (mCarrierTextCallback != null) {
+ mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo(
+ displayText,
+ displayText.toString().split(mSeparator.toString()),
+ anySimReadyAndInService,
+ subsIds));
+ }
+
+ }
+
+ private Context getContext() {
+ return mContext;
+ }
+
+ private String getMissingSimMessage() {
+ return mShowMissingSim && mTelephonyCapable
+ ? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
+ }
+
+ private String getAirplaneModeMessage() {
+ return mShowAirplaneMode
+ ? getContext().getString(R.string.airplane_mode) : "";
+ }
+
+ /**
+ * Top-level function for creating carrier text. Makes text based on simState, PLMN
+ * and SPN as well as device capabilities, such as being emergency call capable.
+ *
+ * @return Carrier text if not in missing state, null otherwise.
+ */
+ private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
+ CharSequence text) {
+ CharSequence carrierText = null;
+ CarrierTextController.StatusMode status = getStatusForIccState(simState);
+ switch (status) {
+ case Normal:
+ carrierText = text;
+ break;
+
+ case SimNotReady:
+ // Null is reserved for denoting missing, in this case we have nothing to display.
+ carrierText = ""; // nothing to display yet.
+ break;
+
+ case NetworkLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ mContext.getText(R.string.keyguard_network_locked_message), text);
+ break;
+
+ case SimMissing:
+ carrierText = null;
+ break;
+
+ case SimPermDisabled:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(
+ R.string.keyguard_permanent_disabled_sim_message_short),
+ text);
+ break;
+
+ case SimMissingLocked:
+ carrierText = null;
+ break;
+
+ case SimLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_sim_locked_message),
+ text);
+ break;
+
+ case SimPukLocked:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_sim_puk_locked_message),
+ text);
+ break;
+ case SimIoError:
+ carrierText = makeCarrierStringOnEmergencyCapable(
+ getContext().getText(R.string.keyguard_sim_error_message_short),
+ text);
+ break;
+ case SimUnknown:
+ carrierText = null;
+ break;
+ }
+
+ return carrierText;
+ }
+
+ /*
+ * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
+ */
+ private CharSequence makeCarrierStringOnEmergencyCapable(
+ CharSequence simMessage, CharSequence emergencyCallMessage) {
+ if (mIsEmergencyCallCapable) {
+ return concatenate(simMessage, emergencyCallMessage, mSeparator);
+ }
+ return simMessage;
+ }
+
+ /**
+ * Determine the current status of the lock screen given the SIM state and other stuff.
+ */
+ private CarrierTextController.StatusMode getStatusForIccState(IccCardConstants.State simState) {
+ // Since reading the SIM may take a while, we assume it is present until told otherwise.
+ if (simState == null) {
+ return CarrierTextController.StatusMode.Normal;
+ }
+
+ final boolean missingAndNotProvisioned =
+ !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
+ && (simState == IccCardConstants.State.ABSENT
+ || simState == IccCardConstants.State.PERM_DISABLED);
+
+ // Assume we're NETWORK_LOCKED if not provisioned
+ simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
+ switch (simState) {
+ case ABSENT:
+ return CarrierTextController.StatusMode.SimMissing;
+ case NETWORK_LOCKED:
+ return CarrierTextController.StatusMode.SimMissingLocked;
+ case NOT_READY:
+ return CarrierTextController.StatusMode.SimNotReady;
+ case PIN_REQUIRED:
+ return CarrierTextController.StatusMode.SimLocked;
+ case PUK_REQUIRED:
+ return CarrierTextController.StatusMode.SimPukLocked;
+ case READY:
+ return CarrierTextController.StatusMode.Normal;
+ case PERM_DISABLED:
+ return CarrierTextController.StatusMode.SimPermDisabled;
+ case UNKNOWN:
+ return CarrierTextController.StatusMode.SimUnknown;
+ case CARD_IO_ERROR:
+ return CarrierTextController.StatusMode.SimIoError;
+ }
+ return CarrierTextController.StatusMode.SimUnknown;
+ }
+
+ private static CharSequence concatenate(CharSequence plmn, CharSequence spn,
+ CharSequence separator) {
+ final boolean plmnValid = !TextUtils.isEmpty(plmn);
+ final boolean spnValid = !TextUtils.isEmpty(spn);
+ if (plmnValid && spnValid) {
+ return new StringBuilder().append(plmn).append(separator).append(spn).toString();
+ } else if (plmnValid) {
+ return plmn;
+ } else if (spnValid) {
+ return spn;
+ } else {
+ return "";
+ }
+ }
+
+ private static List<CharSequence> append(List<CharSequence> list, CharSequence string) {
+ if (!TextUtils.isEmpty(string)) {
+ list.add(string);
+ }
+ return list;
+ }
+
+ private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
+ String plmn, String spn) {
+ int carrierHelpTextId = 0;
+ CarrierTextController.StatusMode status = getStatusForIccState(simState);
+ switch (status) {
+ case NetworkLocked:
+ carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
+ break;
+
+ case SimMissing:
+ carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
+ break;
+
+ case SimPermDisabled:
+ carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
+ break;
+
+ case SimMissingLocked:
+ carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
+ break;
+
+ case Normal:
+ case SimLocked:
+ case SimPukLocked:
+ break;
+ }
+
+ return mContext.getText(carrierHelpTextId);
+ }
+
+ /**
+ * Data structure for passing information to CarrierTextController subscribers
+ */
+ public static final class CarrierTextCallbackInfo {
+ public final CharSequence carrierText;
+ public final CharSequence[] listOfCarriers;
+ public final boolean anySimReady;
+ public final int[] subscriptionIds;
+
+ CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
+ boolean anySimReady, int[] subscriptionIds) {
+ this.carrierText = carrierText;
+ this.listOfCarriers = listOfCarriers;
+ this.anySimReady = anySimReady;
+ this.subscriptionIds = subscriptionIds;
+ }
+ }
+
+ /**
+ * Callback to communicate to Views
+ */
+ public interface CarrierTextCallback {
+ /**
+ * Provides updated carrier information.
+ */
+ default void updateCarrierInfo(CarrierTextCallbackInfo info) {};
+
+ /**
+ * Notifies the View that the device is going to sleep
+ */
+ default void startedGoingToSleep() {};
+
+ /**
+ * Notifies the View that the device finished waking up
+ */
+ default void finishedWakingUp() {};
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index d99f234..fece94e 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -44,6 +44,7 @@
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.AmbientPulseManager;
@@ -278,6 +279,7 @@
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
+ @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
@Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
@Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
@@ -452,6 +454,8 @@
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
mProviders.put(ClockManager.class, mClockManager::get);
+ mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
+
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index b218e80..2339fae 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -30,8 +30,12 @@
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
import com.android.systemui.R
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+import javax.inject.Singleton
-class PrivacyItemController(val context: Context, val callback: Callback) {
+@Singleton
+class PrivacyItemController @Inject constructor(val context: Context) {
companion object {
val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
@@ -56,9 +60,10 @@
private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
private var listening = false
val systemApp = PrivacyApplication(context.getString(R.string.device_services), context)
+ private val callbacks = mutableListOf<WeakReference<Callback>>()
private val notifyChanges = Runnable {
- callback.privacyChanged(privacyList)
+ callbacks.forEach { it.get()?.privacyChanged(privacyList) }
}
private val updateListAndNotifyChanges = Runnable {
@@ -88,8 +93,8 @@
registerReceiver()
}
- init {
- registerReceiver()
+ private fun unregisterReceiver() {
+ context.unregisterReceiver(userSwitcherReceiver)
}
private fun registerReceiver() {
@@ -108,17 +113,41 @@
bgHandler.post(updateListAndNotifyChanges)
}
- fun setListening(listen: Boolean) {
+ @VisibleForTesting
+ internal fun setListening(listen: Boolean) {
if (listening == listen) return
listening = listen
if (listening) {
appOpsController.addCallback(OPS, cb)
+ registerReceiver()
update(true)
} else {
appOpsController.removeCallback(OPS, cb)
+ unregisterReceiver()
}
}
+ private fun addCallback(callback: WeakReference<Callback>) {
+ callbacks.add(callback)
+ if (callbacks.isNotEmpty() && !listening) setListening(true)
+ // Notify this callback if we didn't set to listening
+ else uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
+ }
+
+ private fun removeCallback(callback: WeakReference<Callback>) {
+ // Removes also if the callback is null
+ callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
+ if (callbacks.isEmpty()) setListening(false)
+ }
+
+ fun addCallback(callback: Callback) {
+ addCallback(WeakReference(callback))
+ }
+
+ fun removeCallback(callback: Callback) {
+ removeCallback(WeakReference(callback))
+ }
+
private fun updatePrivacyList() {
privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
.mapNotNull { toPrivacyItem(it) }.distinct()
@@ -149,4 +178,13 @@
}
}
}
+
+ private class NotifyChangesToCallback(
+ private val callback: Callback?,
+ private val list: List<PrivacyItem>
+ ) : Runnable {
+ override fun run() {
+ callback?.privacyChanged(list)
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index e63f88a..c0ed4b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -30,14 +30,18 @@
import android.graphics.drawable.RippleDrawable;
import android.os.Bundle;
import android.os.UserManager;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -45,7 +49,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.keyguard.CarrierText;
+import com.android.keyguard.CarrierTextController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.UserIconDrawable;
@@ -68,7 +72,11 @@
import javax.inject.Named;
public class QSFooterImpl extends FrameLayout implements QSFooter,
- OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback {
+ OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback,
+ CarrierTextController.CarrierTextCallback {
+
+ private static final int SIM_SLOTS = 2;
+ private static final String TAG = "QSFooterImpl";
private final ActivityStarter mActivityStarter;
private final UserInfoController mUserInfoController;
@@ -77,7 +85,6 @@
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
private PageIndicator mPageIndicator;
- private CarrierText mCarrierText;
private boolean mQsDisabled;
private QSPanel mQsPanel;
@@ -99,12 +106,20 @@
private View mActionsContainer;
private View mDragHandle;
- private View mMobileGroup;
- private ImageView mMobileSignal;
- private ImageView mMobileRoaming;
+
+ private View mCarrierDivider;
+ private ViewGroup mMobileFooter;
+ private View[] mMobileGroups = new View[SIM_SLOTS];
+ private ViewGroup[] mCarrierGroups = new ViewGroup[SIM_SLOTS];
+ private TextView[] mCarrierTexts = new TextView[SIM_SLOTS];
+ private ImageView[] mMobileSignals = new ImageView[SIM_SLOTS];
+ private ImageView[] mMobileRoamings = new ImageView[SIM_SLOTS];
+ private final CellSignalState[] mInfos =
+ new CellSignalState[]{new CellSignalState(), new CellSignalState()};
+
private final int mColorForeground;
- private final CellSignalState mInfo = new CellSignalState();
private OnClickListener mExpandClickListener;
+ private CarrierTextController mCarrierTextController;
@Inject
public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -134,10 +149,20 @@
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
- mMobileGroup = findViewById(R.id.mobile_combo);
- mMobileSignal = findViewById(R.id.mobile_signal);
- mMobileRoaming = findViewById(R.id.mobile_roaming);
- mCarrierText = findViewById(R.id.qs_carrier_text);
+ mMobileFooter = findViewById(R.id.qs_mobile);
+ mCarrierGroups[0] = findViewById(R.id.carrier1);
+ mCarrierGroups[1] = findViewById(R.id.carrier2);
+
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ mMobileGroups[i] = mCarrierGroups[i].findViewById(R.id.mobile_combo);
+ mMobileSignals[i] = mCarrierGroups[i].findViewById(R.id.mobile_signal);
+ mMobileRoamings[i] = mCarrierGroups[i].findViewById(R.id.mobile_roaming);
+ mCarrierTexts[i] = mCarrierGroups[i].findViewById(R.id.qs_carrier_text);
+ }
+ mCarrierDivider = findViewById(R.id.qs_carrier_divider);
+ CharSequence separator = mContext.getString(
+ com.android.internal.R.string.kg_text_message_separator);
+ mCarrierTextController = new CarrierTextController(mContext, separator, false, false);
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
@@ -204,8 +229,8 @@
private TouchAnimator createFooterAnimator() {
return new TouchAnimator.Builder()
.addFloat(mDivider, "alpha", 0, 1)
- .addFloat(mCarrierText, "alpha", 0, 0, 1)
- .addFloat(mMobileGroup, "alpha", 0, 1)
+ .addFloat(mMobileFooter, "alpha", 0, 0, 1)
+ .addFloat(mCarrierDivider, "alpha", 0, 1)
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mDragHandle, "alpha", 1, 0, 0)
.addFloat(mPageIndicator, "alpha", 0, 1)
@@ -332,10 +357,12 @@
mNetworkController.addEmergencyListener(this);
mNetworkController.addCallback(this);
}
+ mCarrierTextController.setListening(this);
} else {
mUserInfoController.removeCallback(this);
mNetworkController.removeEmergencyListener(this);
mNetworkController.removeCallback(this);
+ mCarrierTextController.setListening(null);
}
}
@@ -358,7 +385,8 @@
if (v == mSettingsButton) {
if (!mDeviceProvisionedController.isCurrentUserSetup()) {
// If user isn't setup just unlock the device and dump them back at SUW.
- mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+ });
return;
}
MetricsLogger.action(mContext,
@@ -415,32 +443,64 @@
}
private void handleUpdateState() {
- mMobileGroup.setVisibility(mInfo.visible ? View.VISIBLE : View.GONE);
- if (mInfo.visible) {
- mMobileRoaming.setVisibility(mInfo.roaming ? View.VISIBLE : View.GONE);
- mMobileRoaming.setImageTintList(ColorStateList.valueOf(mColorForeground));
- SignalDrawable d = new SignalDrawable(mContext);
- d.setDarkIntensity(QuickStatusBarHeader.getColorIntensity(mColorForeground));
- mMobileSignal.setImageDrawable(d);
- mMobileSignal.setImageLevel(mInfo.mobileSignalIconId);
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ mMobileGroups[i].setVisibility(mInfos[i].visible ? View.VISIBLE : View.GONE);
+ if (mInfos[i].visible) {
+ mMobileRoamings[i].setVisibility(mInfos[i].roaming ? View.VISIBLE : View.GONE);
+ mMobileRoamings[i].setImageTintList(ColorStateList.valueOf(mColorForeground));
+ SignalDrawable d = new SignalDrawable(mContext);
+ d.setDarkIntensity(QuickStatusBarHeader.getColorIntensity(mColorForeground));
+ mMobileSignals[i].setImageDrawable(d);
+ mMobileSignals[i].setImageLevel(mInfos[i].mobileSignalIconId);
- StringBuilder contentDescription = new StringBuilder();
- if (mInfo.contentDescription != null) {
- contentDescription.append(mInfo.contentDescription).append(", ");
+ StringBuilder contentDescription = new StringBuilder();
+ if (mInfos[i].contentDescription != null) {
+ contentDescription.append(mInfos[i].contentDescription).append(", ");
+ }
+ if (mInfos[i].roaming) {
+ contentDescription
+ .append(mContext.getString(R.string.data_connection_roaming))
+ .append(", ");
+ }
+ // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+ if (TextUtils.equals(mInfos[i].typeContentDescription,
+ mContext.getString(R.string.data_connection_no_internet))
+ || TextUtils.equals(mInfos[i].typeContentDescription,
+ mContext.getString(R.string.cell_data_off_content_description))) {
+ contentDescription.append(mInfos[i].typeContentDescription);
+ }
+ mMobileSignals[i].setContentDescription(contentDescription);
}
- if (mInfo.roaming) {
- contentDescription
- .append(mContext.getString(R.string.data_connection_roaming))
- .append(", ");
+ }
+ mCarrierDivider.setVisibility(
+ mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+ if (info.anySimReady) {
+ boolean[] slotSeen = new boolean[SIM_SLOTS];
+ for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
+ int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]);
+ mInfos[slot].visible = true;
+ slotSeen[slot] = true;
+ mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim());
+ mCarrierGroups[slot].setVisibility(View.VISIBLE);
}
- // TODO: show mobile data off/no internet text for 5 seconds before carrier text
- if (TextUtils.equals(mInfo.typeContentDescription,
- mContext.getString(R.string.data_connection_no_internet))
- || TextUtils.equals(mInfo.typeContentDescription,
- mContext.getString(R.string.cell_data_off_content_description))) {
- contentDescription.append(mInfo.typeContentDescription);
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ if (!slotSeen[i]) {
+ mInfos[i].visible = false;
+ mCarrierGroups[i].setVisibility(View.GONE);
+ }
}
- mMobileSignal.setContentDescription(contentDescription);
+ handleUpdateState();
+ } else {
+ mInfos[0].visible = false;
+ mInfos[1].visible = false;
+ mCarrierTexts[0].setText(info.carrierText);
+ mCarrierGroups[0].setVisibility(View.VISIBLE);
+ mCarrierGroups[1].setVisibility(View.GONE);
+ handleUpdateState();
}
}
@@ -450,18 +510,23 @@
int qsType, boolean activityIn, boolean activityOut,
String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {
- mInfo.visible = statusIcon.visible;
- mInfo.mobileSignalIconId = statusIcon.icon;
- mInfo.contentDescription = statusIcon.contentDescription;
- mInfo.typeContentDescription = typeContentDescription;
- mInfo.roaming = roaming;
+ int slotIndex = SubscriptionManager.getSlotIndex(subId);
+ if (slotIndex >= SIM_SLOTS) {
+ Log.e(TAG, "setMobileDataIndicators - slot: " + slotIndex);
+ }
+ mInfos[slotIndex].visible = statusIcon.visible;
+ mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
+ mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
+ mInfos[slotIndex].typeContentDescription = typeContentDescription;
+ mInfos[slotIndex].roaming = roaming;
handleUpdateState();
}
@Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
- mInfo.visible = false;
+ mInfos[0].visible = false;
+ mInfos[1].visible = false;
}
handleUpdateState();
}
@@ -473,4 +538,38 @@
String typeContentDescription;
boolean roaming;
}
+
+
+ /**
+ * TextView that changes its ellipsize value with its visibility.
+ */
+ public static class QSCarrierText extends TextView {
+ public QSCarrierText(Context context) {
+ super(context);
+ }
+
+ public QSCarrierText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // Only show marquee when visible
+ if (visibility == VISIBLE) {
+ setEllipsize(TextUtils.TruncateAt.MARQUEE);
+ } else {
+ setEllipsize(TextUtils.TruncateAt.END);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 75ab5df..2d64ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -180,14 +180,14 @@
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NextAlarmController nextAlarmController, ZenModeController zenModeController,
BatteryController batteryController, StatusBarIconController statusBarIconController,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter, PrivacyItemController privacyItemController) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
mBatteryController = batteryController;
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
- mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
+ mPrivacyItemController = privacyItemController;
mShownCount = getStoredShownCount();
}
@@ -512,7 +512,6 @@
return;
}
mHeaderQsPanel.setListening(listening);
- mPrivacyItemController.setListening(listening);
mListening = listening;
if (listening) {
@@ -520,9 +519,11 @@
mAlarmController.addCallback(this);
mContext.registerReceiver(mRingerReceiver,
new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+ mPrivacyItemController.addCallback(mPICCallback);
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
+ mPrivacyItemController.removeCallback(mPICCallback);
mContext.unregisterReceiver(mRingerReceiver);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 6c3e504..04534ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -19,6 +19,7 @@
import android.view.View;
import com.android.systemui.Interpolators;
+import com.android.systemui.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
@@ -92,9 +93,15 @@
private static void updateLayerType(View view, float alpha) {
if (view.hasOverlappingRendering() && alpha > 0.0f && alpha < 1.0f) {
- view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE) {
- view.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (view.getLayerType() != View.LAYER_TYPE_HARDWARE) {
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ view.setTag(R.id.cross_fade_layer_type_changed_tag, true);
+ }
+ } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE
+ && view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
+ if (view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
}
@@ -114,7 +121,7 @@
.setStartDelay(delay)
.setInterpolator(Interpolators.ALPHA_IN)
.withEndAction(null);
- if (view.hasOverlappingRendering()) {
+ if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) {
view.animate().withLayer();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
new file mode 100644
index 0000000..4944732
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.systemui.statusbar
+
+import android.annotation.ColorInt
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Point
+import android.graphics.Rect
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.ScriptIntrinsicBlur
+import android.util.MathUtils
+import com.android.internal.graphics.ColorUtils
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private const val COLOR_ALPHA = (255 * 0.7f).toInt()
+private const val BLUR_RADIUS = 25f
+private const val DOWNSAMPLE = 6
+
+@Singleton
+class MediaArtworkProcessor @Inject constructor() {
+
+ private val mTmpSize = Point()
+ private var mArtworkCache: Bitmap? = null
+
+ fun processArtwork(context: Context, artwork: Bitmap, @ColorInt color: Int): Bitmap {
+ if (mArtworkCache != null) {
+ return mArtworkCache!!
+ }
+
+ context.display.getSize(mTmpSize)
+ val renderScript = RenderScript.create(context)
+ val rect = Rect(0, 0,artwork.width, artwork.height)
+ MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
+ val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
+ true /* filter */)
+ val input = Allocation.createFromBitmap(renderScript, inBitmap,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
+ val outBitmap = Bitmap.createBitmap(inBitmap.width, inBitmap.height,
+ Bitmap.Config.ARGB_8888)
+ val output = Allocation.createFromBitmap(renderScript, outBitmap)
+ val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
+ blur.setRadius(BLUR_RADIUS)
+ blur.setInput(input)
+ blur.forEach(output)
+ output.copyTo(outBitmap)
+
+ input.destroy()
+ output.destroy()
+ inBitmap.recycle()
+
+ val canvas = Canvas(outBitmap)
+ canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
+ return outBitmap
+ }
+
+ fun clearCache() {
+ mArtworkCache?.recycle()
+ mArtworkCache = null
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index f46ded4d..c25b7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -39,6 +39,9 @@
boolean isCurrentProfile(int userId);
+ /** Adds a listener to be notified when the current user changes. */
+ void addUserChangedListener(UserChangedListener listener);
+
void destroy();
SparseArray<UserInfo> getCurrentProfiles();
@@ -58,4 +61,9 @@
boolean needsRedaction(NotificationEntry entry);
boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
+
+ /** Notified when the current user changes. */
+ interface UserChangedListener {
+ void onUserChanged(int userId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index d2ce31d..4f9d428 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -55,6 +55,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Handles keeping track of the current user, profiles, and various things related to hiding
@@ -78,6 +80,7 @@
private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
private final UserManager mUserManager;
private final IStatusBarService mBarService;
+ private final List<UserChangedListener> mListeners = new ArrayList<>();
private boolean mShowLockscreenNotifications;
private boolean mAllowLockscreenRemoteInput;
@@ -112,6 +115,10 @@
updatePublicMode();
mPresenter.onUserSwitched(mCurrentUserId);
getEntryManager().getNotificationData().filterAndSort();
+
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserChanged(mCurrentUserId);
+ }
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
updateCurrentProfilesCache();
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
@@ -502,6 +509,10 @@
}
}
+ @Override
+ public void addUserChangedListener(UserChangedListener listener) {
+ mListeners.add(listener);
+ }
// public void updatePublicMode() {
// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
@@ -541,6 +552,7 @@
public void destroy() {
mContext.unregisterReceiver(mBaseBroadcastReceiver);
mContext.unregisterReceiver(mAllUsersReceiver);
+ mListeners.clear();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 7412702..98a3a54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -25,6 +25,7 @@
import android.app.Notification;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -102,6 +103,7 @@
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
private final ArrayList<MediaListener> mMediaListeners;
+ private final MediaArtworkProcessor mMediaArtworkProcessor;
protected NotificationPresenter mPresenter;
private MediaController mMediaController;
@@ -133,6 +135,7 @@
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
+ mMediaArtworkProcessor.clearCache();
mMediaMetadata = metadata;
dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
@@ -143,8 +146,10 @@
Context context,
Lazy<ShadeController> shadeController,
Lazy<StatusBarWindowController> statusBarWindowController,
- NotificationEntryManager notificationEntryManager) {
+ NotificationEntryManager notificationEntryManager,
+ MediaArtworkProcessor mediaArtworkProcessor) {
mContext = context;
+ mMediaArtworkProcessor = mediaArtworkProcessor;
mMediaListeners = new ArrayList<>();
mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -366,6 +371,7 @@
}
private void clearCurrentMediaNotificationSession() {
+ mMediaArtworkProcessor.clearCache();
mMediaMetadata = null;
if (mMediaController != null) {
if (DEBUG_MEDIA) {
@@ -418,7 +424,19 @@
// might still be null
}
if (artworkBitmap != null) {
- artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
+ int notificationColor;
+ synchronized (mEntryManager.getNotificationData()) {
+ NotificationEntry entry = mEntryManager.getNotificationData()
+ .get(mMediaNotificationKey);
+ if (entry == null || entry.getRow() == null) {
+ notificationColor = Color.TRANSPARENT;
+ } else {
+ notificationColor = entry.getRow().calculateBgColor();
+ }
+ }
+ Bitmap bmp = mMediaArtworkProcessor.processArtwork(mContext, artworkBitmap,
+ notificationColor);
+ artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
}
}
boolean allowWhenShade = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 8c29fb5..54ed0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -333,6 +333,7 @@
mGroupManager.onEntryUpdated(entry, oldSbn);
}
entry.populateFromRanking(mTmpRanking);
+ entry.setIsHighPriority(isHighPriority(entry.notification));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index ee551ee..2e93c382 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -153,6 +153,12 @@
*/
private boolean mUserDismissedBubble;
+ /**
+ * Whether this notification is shown to the user as a high priority notification: visible on
+ * the lock screen/status bar and in the top section in the shade.
+ */
+ private boolean mHighPriority;
+
public NotificationEntry(StatusBarNotification n) {
this(n, null);
}
@@ -191,6 +197,14 @@
return interruption;
}
+ public boolean isHighPriority() {
+ return mHighPriority;
+ }
+
+ public void setIsHighPriority(boolean highPriority) {
+ this.mHighPriority = highPriority;
+ }
+
public void setIsBubble(boolean bubbleable) {
mIsBubble = bubbleable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 7b94c74..35b7ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -167,13 +167,10 @@
*/
public static NotificationVisibility.NotificationLocation getNotificationLocation(
NotificationEntry entry) {
- ExpandableNotificationRow row = entry.getRow();
- ExpandableViewState childViewState = row.getViewState();
-
- if (childViewState == null) {
+ if (entry == null || entry.getRow() == null || entry.getRow().getViewState() == null) {
return NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN;
}
- return convertNotificationLocation(childViewState.location);
+ return convertNotificationLocation(entry.getRow().getViewState().location);
}
private static NotificationVisibility.NotificationLocation convertNotificationLocation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 296c061..bed2426 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -3092,6 +3092,11 @@
}
}
+ /** Sets whether dismiss gestures are right-to-left (instead of left-to-right). */
+ public void setDismissRtl(boolean dismissRtl) {
+ mMenuRow.setDismissRtl(dismissRtl);
+ }
+
private static class NotificationViewState extends ExpandableViewState {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 5e9f207..cb1384c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -300,7 +300,8 @@
row.getIsNonblockable(),
isForBlockingHelper,
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
- row.getEntry().importance);
+ row.getEntry().importance,
+ row.getEntry().isHighPriority());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 5253e38..2a9a815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -21,7 +21,6 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -97,8 +96,12 @@
private int mNumUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
- private int mStartingChannelOrNotificationImportance;
- private int mChosenImportance;
+ private boolean mWasShownHighPriority;
+ /**
+ * The last importance level chosen by the user. Null if the user has not chosen an importance
+ * level; non-null once the user takes an action which indicates an explicit preference.
+ */
+ @Nullable private Integer mChosenImportance;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
private StatusBarNotification mSbn;
@@ -195,13 +198,14 @@
final OnAppSettingsClickListener onAppSettingsClick,
boolean isDeviceProvisioned,
boolean isNonblockable,
- int importance)
+ int importance,
+ boolean wasShownHighPriority)
throws RemoteException {
bindNotification(pm, iNotificationManager, pkg, notificationChannel,
numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */, false /* isUserSentimentNegative */,
- importance);
+ importance, wasShownHighPriority);
}
public void bindNotification(
@@ -218,7 +222,8 @@
boolean isNonblockable,
boolean isForBlockingHelper,
boolean isUserSentimentNegative,
- int importance)
+ int importance,
+ boolean wasShownHighPriority)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -231,10 +236,8 @@
mCheckSaveListener = checkSaveListener;
mOnSettingsClickListener = onSettingsClick;
mSingleNotificationChannel = notificationChannel;
- int channelImportance = mSingleNotificationChannel.getImportance();
- mStartingChannelImportance = mChosenImportance = channelImportance;
- mStartingChannelOrNotificationImportance =
- channelImportance == IMPORTANCE_UNSPECIFIED ? importance : channelImportance;
+ mStartingChannelImportance = mSingleNotificationChannel.getImportance();
+ mWasShownHighPriority = wasShownHighPriority;
mNegativeUserSentiment = isUserSentimentNegative;
mIsNonblockable = isNonblockable;
mIsForeground =
@@ -400,19 +403,27 @@
* @return new LogMaker
*/
private LogMaker importanceChangeLogMaker() {
+ Integer chosenImportance =
+ mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
return new LogMaker(MetricsEvent.ACTION_SAVE_IMPORTANCE)
.setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(mChosenImportance - mStartingChannelImportance);
+ .setSubtype(chosenImportance - mStartingChannelImportance);
}
private boolean hasImportanceChanged() {
return mSingleNotificationChannel != null
- && mStartingChannelImportance != mChosenImportance;
+ && mChosenImportance != null
+ && (mStartingChannelImportance != mChosenImportance
+ || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)
+ || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT));
}
private void saveImportance() {
if (!mIsNonblockable
|| mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
+ if (mChosenImportance == null) {
+ mChosenImportance = mStartingChannelImportance;
+ }
updateImportance();
}
}
@@ -421,12 +432,15 @@
* Commits the updated importance values on the background thread.
*/
private void updateImportance() {
- mMetricsLogger.write(importanceChangeLogMaker());
+ if (mChosenImportance != null) {
+ mMetricsLogger.write(importanceChangeLogMaker());
- Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
- bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
- mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
- mStartingChannelImportance, mChosenImportance));
+ Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+ bgHandler.post(
+ new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
+ mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+ mStartingChannelImportance, mChosenImportance));
+ }
}
private void bindButtons() {
@@ -444,11 +458,8 @@
TextView silent = findViewById(R.id.int_silent);
TextView alert = findViewById(R.id.int_alert);
- boolean isCurrentlyAlerting =
- mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT;
-
block.setOnClickListener(mOnStopOrMinimizeNotifications);
- if (isCurrentlyAlerting) {
+ if (mWasShownHighPriority) {
silent.setOnClickListener(mOnToggleSilent);
silent.setText(R.string.inline_silent_button_silent);
alert.setOnClickListener(mOnKeepShowing);
@@ -517,7 +528,7 @@
break;
case ACTION_TOGGLE_SILENT:
mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
- if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) {
+ if (mWasShownHighPriority) {
mChosenImportance = IMPORTANCE_LOW;
confirmationText.setText(R.string.notification_channel_silenced);
} else {
@@ -584,9 +595,8 @@
@Override
public void onFinishedClosing() {
- mStartingChannelImportance = mChosenImportance;
- if (mChosenImportance != IMPORTANCE_UNSPECIFIED) {
- mStartingChannelOrNotificationImportance = mChosenImportance;
+ if (mChosenImportance != null) {
+ mStartingChannelImportance = mChosenImportance;
}
mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index d97162c..d83a158 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -23,7 +23,6 @@
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -78,6 +77,8 @@
private ArrayList<MenuItem> mRightMenuItems;
private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
+ private boolean mDismissRtl;
+ private boolean mIsForeground;
private ValueAnimator mFadeAnimator;
private boolean mAnimating;
@@ -238,6 +239,8 @@
}
private void createMenuViews(boolean resetState, final boolean isForeground) {
+ mIsForeground = isForeground;
+
final Resources res = mContext.getResources();
mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -250,12 +253,7 @@
}
mAppOpsItem = createAppOpsItem(mContext);
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- int channelImportance = mParent.getEntry().channel.getImportance();
- int effectiveImportance =
- channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
- ? mParent.getEntry().importance : channelImportance;
- mInfoItem = createInfoItem(mContext,
- effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT);
+ mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority());
} else {
mInfoItem = createInfoItem(mContext);
}
@@ -268,10 +266,11 @@
mRightMenuItems.add(mAppOpsItem);
mLeftMenuItems.addAll(mRightMenuItems);
} else {
- mRightMenuItems.add(mInfoItem);
- mRightMenuItems.add(mAppOpsItem);
+ ArrayList<MenuItem> menuItems = mDismissRtl ? mLeftMenuItems : mRightMenuItems;
+ menuItems.add(mInfoItem);
+ menuItems.add(mAppOpsItem);
if (!isForeground) {
- mRightMenuItems.add(mSnoozeItem);
+ menuItems.add(mSnoozeItem);
}
}
@@ -729,6 +728,14 @@
return getParent().canViewBeDismissed();
}
+ @Override
+ public void setDismissRtl(boolean dismissRtl) {
+ mDismissRtl = dismissRtl;
+ if (mMenuContainer != null) {
+ createMenuViews(true, mIsForeground);
+ }
+ }
+
public static class NotificationMenuItem implements MenuItem {
View mMenuView;
GutsContent mGutsContent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index db7b4fc..4bdc170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -17,8 +17,15 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.os.Build;
import android.view.View;
+import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -42,6 +49,47 @@
}
@Override
+ public void onReinflated() {
+ super.onReinflated();
+
+ Configuration configuration = mView.getResources().getConfiguration();
+ boolean nightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+
+ float[] hsl = new float[] {0f, 0f, 0f};
+ ColorUtils.colorToHSL(mBackgroundColor, hsl);
+ boolean backgroundIsDark = Color.alpha(mBackgroundColor) == 0
+ || hsl[1] == 0 && hsl[2] < 0.5;
+ boolean backgroundHasColor = hsl[1] > 0;
+
+ // Let's invert the notification colors when we're in night mode and
+ // the notification background isn't colorized.
+ if (!backgroundIsDark && !backgroundHasColor && nightMode
+ && mRow.getEntry().targetSdk < Build.VERSION_CODES.Q) {
+ Paint paint = new Paint();
+ ColorMatrix matrix = new ColorMatrix();
+ ColorMatrix tmp = new ColorMatrix();
+ // Inversion should happen on Y'UV space to conseve the colors and
+ // only affect the luminosity.
+ matrix.setRGB2YUV();
+ tmp.set(new float[]{
+ -1f, 0f, 0f, 0f, 255f,
+ 0f, 1f, 0f, 0f, 0f,
+ 0f, 0f, 1f, 0f, 0f,
+ 0f, 0f, 0f, 1f, 0f
+ });
+ matrix.postConcat(tmp);
+ tmp.setYUV2RGB();
+ matrix.postConcat(tmp);
+ paint.setColorFilter(new ColorMatrixColorFilter(matrix));
+ mView.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
+
+ hsl[2] = 1f - hsl[2];
+ mBackgroundColor = ColorUtils.HSLToColor(hsl);
+ }
+ }
+
+ @Override
protected boolean shouldClearBackgroundOnReapply() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 1efdc56..9258c99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -37,7 +37,7 @@
protected final View mView;
protected final ExpandableNotificationRow mRow;
- private int mBackgroundColor = 0;
+ protected int mBackgroundColor = 0;
public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 2129b81..63b34d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -174,6 +174,7 @@
private final boolean mShouldDrawNotificationBackground;
private boolean mLowPriorityBeforeSpeedBump;
private final boolean mAllowLongPress;
+ private boolean mDismissRtl;
private float mExpandedHeight;
private int mOwnScrollY;
@@ -533,8 +534,10 @@
tunerService.addTunable((key, newValue) -> {
if (key.equals(LOW_PRIORITY)) {
mLowPriorityBeforeSpeedBump = "1".equals(newValue);
+ } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
+ updateDismissRtlSetting("1".equals(newValue));
}
- }, LOW_PRIORITY);
+ }, LOW_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL);
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
@@ -548,6 +551,16 @@
});
}
+ private void updateDismissRtlSetting(boolean dismissRtl) {
+ mDismissRtl = dismissRtl;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
+ }
+ }
+ }
+
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onFinishInflate() {
@@ -2600,8 +2613,7 @@
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification())) {
+ if (!row.getEntry().isHighPriority()) {
break;
} else {
lastChildBeforeGap = row;
@@ -2619,8 +2631,7 @@
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification())) {
+ if (!row.getEntry().isHighPriority()) {
return row;
}
}
@@ -3255,6 +3266,9 @@
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
updateChronometerForChild(child);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
+ }
if (ANCHOR_SCROLLING) {
// TODO: once we're recycling this will need to check the adapter position of the child
if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) {
@@ -5756,11 +5770,9 @@
currentIndex++;
boolean beforeSpeedBump;
if (mLowPriorityBeforeSpeedBump) {
- beforeSpeedBump = !mEntryManager.getNotificationData().isAmbient(
- row.getStatusBarNotification().getKey());
+ beforeSpeedBump = !row.getEntry().ambient;
} else {
- beforeSpeedBump = mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification());
+ beforeSpeedBump = row.getEntry().isHighPriority();
}
if (beforeSpeedBump) {
speedBumpIndex = currentIndex;
@@ -5784,8 +5796,7 @@
continue;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (!mEntryManager.getNotificationData().isHighPriority(
- row.getStatusBarNotification())) {
+ if (!row.getEntry().isHighPriority()) {
if (currentIndex > 0) {
gapIndex = currentIndex;
}
@@ -6315,7 +6326,7 @@
public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
boolean isValidDirection;
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- isValidDirection = isLayoutRtl() ? !isRightOrDown : isRightOrDown;
+ isValidDirection = mDismissRtl ? !isRightOrDown : isRightOrDown;
} else {
isValidDirection = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index a5d9382..39fbbb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -20,6 +20,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
+import static com.android.systemui.statusbar.phone.NavigationPrototypeController.PROTOTYPE_ENABLED;
import android.annotation.NonNull;
import android.content.Context;
@@ -84,7 +85,7 @@
// Tell launcher that this action requires a stable task list or not
boolean flag = requiresStableTaskList();
- if (flag != sLastTaskStabilizationFlag) {
+ if (getGlobalBoolean(PROTOTYPE_ENABLED) && flag != sLastTaskStabilizationFlag) {
Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
ENABLE_TASK_STABILIZER_FLAG, flag ? 1 : 0);
sLastTaskStabilizationFlag = flag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index a09e585..f762a6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -37,6 +37,7 @@
private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
+ public static final String PROTOTYPE_ENABLED = "prototype_enabled";
@Retention(RetentionPolicy.SOURCE)
@IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 077fcda..e86996a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -194,8 +194,7 @@
if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
return false;
}
- if (!showLowPriority
- && !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
+ if (!showLowPriority && !entry.isHighPriority()) {
return false;
}
if (!entry.isTopLevelChild()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 43c35f1..18711c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -173,7 +173,7 @@
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mLocationController = Dependency.get(LocationController.class);
- mPrivacyItemController = new PrivacyItemController(mContext, this);
+ mPrivacyItemController = Dependency.get(PrivacyItemController.class);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -266,7 +266,7 @@
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
- mPrivacyItemController.setListening(true);
+ mPrivacyItemController.addCallback(this);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
@@ -294,7 +294,7 @@
mNextAlarmController.removeCallback(mNextAlarmCallback);
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
- mPrivacyItemController.setListening(false);
+ mPrivacyItemController.removeCallback(this);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
mContext.unregisterReceiver(mIntentReceiver);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index e6d7ee7..98bf3c27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -45,9 +45,12 @@
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -73,6 +76,8 @@
private lateinit var userManager: UserManager
@Captor
private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
+ @Captor
+ private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
@@ -95,7 +100,16 @@
}
})).`when`(userManager).getProfiles(anyInt())
- privacyItemController = PrivacyItemController(mContext, callback)
+ privacyItemController = PrivacyItemController(mContext)
+ }
+
+ @Test
+ fun testSetListeningTrueByAddingCallback() {
+ privacyItemController.addCallback(callback)
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+ any(AppOpsController.Callback::class.java))
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(anyList())
}
@Test
@@ -103,8 +117,6 @@
privacyItemController.setListening(true)
verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
any(AppOpsController.Callback::class.java))
- testableLooper.processAllMessages()
- verify(callback).privacyChanged(anyList())
}
@Test
@@ -121,7 +133,7 @@
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
.`when`(appOpsController).getActiveAppOpsForUser(anyInt())
- privacyItemController.setListening(true)
+ privacyItemController.addCallback(callback)
testableLooper.processAllMessages()
verify(callback).privacyChanged(capture(argCaptor))
assertEquals(1, argCaptor.value.size)
@@ -131,7 +143,7 @@
fun testSystemApps() {
doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
- privacyItemController.setListening(true)
+ privacyItemController.addCallback(callback)
testableLooper.processAllMessages()
verify(callback).privacyChanged(capture(argCaptor))
assertEquals(1, argCaptor.value.size)
@@ -142,8 +154,8 @@
@Test
fun testRegisterReceiver_allUsers() {
val spiedContext = spy(mContext)
- val itemController = PrivacyItemController(spiedContext, callback)
-
+ val itemController = PrivacyItemController(spiedContext)
+ itemController.setListening(true)
verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
eq(null))
@@ -170,4 +182,54 @@
Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
verify(userManager).getProfiles(anyInt())
}
+
+ @Test
+ fun testAddMultipleCallbacks() {
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(anyList())
+
+ privacyItemController.addCallback(otherCallback)
+ testableLooper.processAllMessages()
+ verify(otherCallback).privacyChanged(anyList())
+ // Adding a callback should not unnecessarily call previous ones
+ verifyNoMoreInteractions(callback)
+ }
+
+ @Test
+ fun testMultipleCallbacksAreUpdated() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ testableLooper.processAllMessages()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(anyList())
+ verify(otherCallback).privacyChanged(anyList())
+ }
+
+ @Test
+ fun testRemoveCallback() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ testableLooper.processAllMessages()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
+ privacyItemController.removeCallback(callback)
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ testableLooper.processAllMessages()
+ verify(callback, never()).privacyChanged(anyList())
+ verify(otherCallback).privacyChanged(anyList())
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 40d2da9..fdc9e0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -354,7 +354,8 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
@@ -382,16 +383,18 @@
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
- public void testInitializeNotificationInfoView_importance() throws Exception {
+ public void testInitializeNotificationInfoView_highPriority() throws Exception {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(true);
row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
row.getEntry().importance = IMPORTANCE_DEFAULT;
+ row.getEntry().setIsHighPriority(true);
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
@@ -411,7 +414,8 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(IMPORTANCE_DEFAULT));
+ eq(IMPORTANCE_DEFAULT),
+ eq(true) /* wasShownHighPriority */);
}
@Test
@@ -440,7 +444,8 @@
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
@@ -468,7 +473,8 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(0));
+ eq(0),
+ eq(false) /* wasShownHighPriority */);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f6791dd5..08955e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -210,7 +210,7 @@
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -223,7 +223,7 @@
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -232,7 +232,7 @@
public void testBindNotification_noDelegate() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
@@ -251,7 +251,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Other"));
@@ -263,7 +263,7 @@
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -280,7 +280,7 @@
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -292,7 +292,7 @@
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -301,7 +301,7 @@
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -314,7 +314,7 @@
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -323,7 +323,7 @@
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -332,7 +332,7 @@
public void testBindNotification_BlockButton() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final View block = mNotificationInfo.findViewById(R.id.int_block);
final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize);
assertEquals(VISIBLE, block.getVisibility());
@@ -343,7 +343,7 @@
public void testBindNotification_BlockButton_BlockHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT);
+ true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT, true);
final View block = mNotificationInfo.findViewById(R.id.block);
final View interruptivenessSettings = mNotificationInfo.findViewById(
R.id.interruptiveness_settings);
@@ -356,7 +356,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
@@ -368,7 +368,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
@@ -381,7 +381,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
@@ -393,7 +393,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
@@ -405,7 +405,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, silent.getVisibility());
@@ -421,7 +421,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
assertEquals(VISIBLE, silent.getVisibility());
@@ -437,7 +437,7 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final View block = mNotificationInfo.findViewById(R.id.block);
final View interruptivenessSettings = mNotificationInfo.findViewById(
R.id.interruptiveness_settings);
@@ -455,7 +455,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, IMPORTANCE_DEFAULT);
+ }, null, true, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -467,7 +467,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -479,7 +479,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, IMPORTANCE_DEFAULT);
+ }, null, false, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -488,11 +488,11 @@
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, IMPORTANCE_DEFAULT);
+ }, null, true, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -501,7 +501,7 @@
public void testLogBlockingHelperCounter_logGutsViewDisplayed() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getType() == MetricsEvent.NOTIFICATION_BLOCKING_HELPER
@@ -513,7 +513,7 @@
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
- true, true, IMPORTANCE_DEFAULT);
+ true, true, IMPORTANCE_DEFAULT, true);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1));
}
@@ -526,7 +526,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, IMPORTANCE_DEFAULT);
+ }, null, true, true, IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -539,7 +539,7 @@
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, IMPORTANCE_DEFAULT);
+ null, true, true, IMPORTANCE_DEFAULT, true);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -550,7 +550,7 @@
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, IMPORTANCE_DEFAULT);
+ null, true, true, IMPORTANCE_DEFAULT, true);
final TextView blockView = mNotificationInfo.findViewById(R.id.block);
assertEquals(GONE, blockView.getVisibility());
}
@@ -559,7 +559,7 @@
public void testbindNotification_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
- true, true, IMPORTANCE_DEFAULT);
+ true, true, IMPORTANCE_DEFAULT, true);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -569,7 +569,7 @@
public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -580,7 +580,7 @@
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -591,7 +591,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
@@ -605,7 +605,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
mTestableLooper.processAllMessages();
@@ -619,7 +619,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
mTestableLooper.processAllMessages();
@@ -633,7 +633,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
mTestableLooper.processAllMessages();
@@ -647,7 +647,7 @@
int originalImportance = mNotificationChannel.getImportance();
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -662,7 +662,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true, false);
@@ -680,7 +680,7 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
- true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT, false
);
mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -702,7 +702,7 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */,
- true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT, false
);
mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -724,7 +724,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -752,7 +752,7 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -781,7 +781,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
false /* isNonblockable */, true /* isForBlockingHelper */,
true, true /* isUserSentimentNegative */, /* isNoisy */
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -800,7 +800,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -823,7 +823,7 @@
true /* isForBlockingHelper */,
true,
false /* isUserSentimentNegative */,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -838,7 +838,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -852,7 +852,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -888,7 +888,8 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true /* isUserSentimentNegative */,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT,
+ false);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -913,7 +914,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -928,7 +929,7 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -949,7 +950,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.handleCloseControls(true, false);
@@ -967,7 +968,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -988,7 +989,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1006,7 +1007,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
@@ -1027,7 +1028,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
@@ -1049,7 +1050,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
@@ -1071,7 +1072,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_LOW);
+ IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
@@ -1092,7 +1093,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1108,7 +1109,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -1125,7 +1126,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
- }, null, null, true, true, IMPORTANCE_DEFAULT);
+ }, null, null, true, true, IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
@@ -1143,7 +1144,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_DEFAULT
+ }, null, null, true, false, IMPORTANCE_DEFAULT, false
);
mNotificationInfo.findViewById(R.id.int_block).performClick();
@@ -1170,7 +1171,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1183,7 +1184,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -1196,7 +1197,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
@@ -1210,7 +1211,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
@@ -1224,7 +1225,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
@@ -1236,7 +1237,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- IMPORTANCE_DEFAULT);
+ IMPORTANCE_DEFAULT, false);
mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 736f384..ae70b01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -352,7 +352,7 @@
RETURNS_DEEP_STUBS);
String key = Integer.toString(i);
when(row.getStatusBarNotification().getKey()).thenReturn(key);
- when(mNotificationData.isHighPriority(row.getStatusBarNotification())).thenReturn(true);
+ when(row.getEntry().isHighPriority()).thenReturn(true);
when(mStackScroller.getChildAt(i)).thenReturn(row);
}
@@ -368,8 +368,7 @@
RETURNS_DEEP_STUBS);
String key = Integer.toString(i);
when(row.getStatusBarNotification().getKey()).thenReturn(key);
- when(mNotificationData.isHighPriority(row.getStatusBarNotification()))
- .thenReturn(false);
+ when(row.getEntry().isHighPriority()).thenReturn(false);
when(mStackScroller.getChildAt(i)).thenReturn(row);
}
@@ -385,8 +384,7 @@
RETURNS_DEEP_STUBS);
String key = Integer.toString(i);
when(row.getStatusBarNotification().getKey()).thenReturn(key);
- when(mNotificationData.isHighPriority(row.getStatusBarNotification()))
- .thenReturn(i < 3);
+ when(row.getEntry().isHighPriority()).thenReturn(i < 3);
when(mStackScroller.getChildAt(i)).thenReturn(row);
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index d1aa84f..73fcb01 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6780,22 +6780,22 @@
CONVERSATION_ACTIONS = 1615;
// ACTION: Actions from a text classifier are shown to user.
- // CATEGORY: CONVERSATION_ACTIONS
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN = 1616;
- // ACTION: Event time of a text classifier event in unix timestamp.
- // CATEGORY: CONVERSATION_ACTIONS, LANGUAGE_DETECTION
+ // FIELD: Event time of a text classifier event in unix timestamp.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
FIELD_TEXT_CLASSIFIER_EVENT_TIME = 1617;
// ACTION: Users compose their own replies instead of using suggested ones.
- // CATEGORY: CONVERSATION_ACTIONS
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
ACTION_TEXT_CLASSIFIER_MANUAL_REPLY = 1618;
// ACTION: Text classifier generates an action.
- // CATEGORY: CONVERSATION_ACTIONS
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
// OS: Q
ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED = 1619;
@@ -6840,7 +6840,7 @@
// OPEN: Settings > Display > Adaptive sleep
// OS: Q
SETTINGS_ADAPTIVE_SLEEP = 1628;
-
+
// Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
// The UI location of the notification containing the smart suggestions.
// This is a NotificationLocation object (see the NotificationLocation
@@ -6870,6 +6870,40 @@
// OS: Q
DIALOG_AWARE_DISABLE = 1633;
+ // FIELD: Session ID of TextClassifierEvent.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_SESSION_ID = 1634;
+
+ // FIELD: First entity type.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE = 1635;
+ // FIELD: Second entity type.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE = 1636;
+
+ // FIELD: Third entity type.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE = 1637;
+
+ // FIELD: Score of the suggestion.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_SCORE = 1638;
+
+ // FIELD: widget type, e.g: notification, textview
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_WIDGET_TYPE = 1639;
+
+ // FIELD: version of the widget.
+ // CATEGORY: LANGUAGE_DETECTION, CONVERSATION_ACTIONS
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_WIDGET_VERSION = 1640;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index bcff4e0..303230b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -402,7 +402,7 @@
MagnificationGestureHandler magnificationGestureHandler =
new MagnificationGestureHandler(mContext,
mAms.getMagnificationController(),
- detectControlGestures, triggerable);
+ detectControlGestures, triggerable, displayId);
addFirstEventHandler(displayId, magnificationGestureHandler);
mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 49db488..2fbaee6 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -43,7 +43,6 @@
import android.util.MathUtils;
import android.util.Slog;
import android.util.TypedValue;
-import android.view.Display;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
@@ -149,6 +148,8 @@
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
+ private final int mDisplayId;
+
private final Queue<MotionEvent> mDebugInputEventHistory;
private final Queue<MotionEvent> mDebugOutputEventHistory;
@@ -162,11 +163,13 @@
* @param detectShortcutTrigger {@code true} if this detector should be "triggerable" by some
* external shortcut invoking {@link #notifyShortcutTriggered},
* {@code false} if it should ignore such triggers.
+ * @param displayId The logical display id.
*/
public MagnificationGestureHandler(Context context,
MagnificationController magnificationController,
boolean detectTripleTap,
- boolean detectShortcutTrigger) {
+ boolean detectShortcutTrigger,
+ int displayId) {
if (DEBUG_ALL) {
Log.i(LOG_TAG,
"MagnificationGestureHandler(detectTripleTap = " + detectTripleTap
@@ -174,6 +177,7 @@
}
mMagnificationController = magnificationController;
+ mDisplayId = displayId;
mDelegatingState = new DelegatingState();
mDetectingState = new DetectingState(context);
@@ -259,8 +263,7 @@
void notifyShortcutTriggered() {
if (mDetectShortcutTrigger) {
- // TODO: multi-display support for magnification gesture handler
- boolean wasMagnifying = mMagnificationController.resetIfNeeded(Display.DEFAULT_DISPLAY,
+ boolean wasMagnifying = mMagnificationController.resetIfNeeded(mDisplayId,
/* animate */ true);
if (wasMagnifying) {
clearAndTransitionToStateDetecting();
@@ -422,8 +425,7 @@
Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
+ " scrollY: " + distanceY);
}
- // TODO: multi-display support for magnification gesture handler
- mMagnificationController.offsetMagnifiedRegion(Display.DEFAULT_DISPLAY, distanceX,
+ mMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return /* event consumed: */ true;
}
@@ -440,8 +442,7 @@
return mScaling;
}
- // TODO: multi-display support for magnification gesture handler
- final float initialScale = mMagnificationController.getScale(Display.DEFAULT_DISPLAY);
+ final float initialScale = mMagnificationController.getScale(mDisplayId);
final float targetScale = initialScale * detector.getScaleFactor();
// Don't allow a gesture to move the user further outside the
@@ -463,8 +464,7 @@
final float pivotX = detector.getFocusX();
final float pivotY = detector.getFocusY();
if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x");
- // TODO: multi-display support for magnification gesture handler
- mMagnificationController.setScale(Display.DEFAULT_DISPLAY, scale, pivotX, pivotY, false,
+ mMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return /* handled: */ true;
}
@@ -524,10 +524,9 @@
}
final float eventX = event.getX();
final float eventY = event.getY();
- // TODO: multi-display support for magnification gesture handler
if (mMagnificationController.magnificationRegionContains(
- Display.DEFAULT_DISPLAY, eventX, eventY)) {
- mMagnificationController.setCenter(Display.DEFAULT_DISPLAY, eventX, eventY,
+ mDisplayId, eventX, eventY)) {
+ mMagnificationController.setCenter(mDisplayId, eventX, eventY,
/* animate */ mLastMoveOutsideMagnifiedRegion,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
mLastMoveOutsideMagnifiedRegion = false;
@@ -665,9 +664,8 @@
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
- // TODO: multi-display support for magnification gesture handler
if (!mMagnificationController.magnificationRegionContains(
- Display.DEFAULT_DISPLAY, event.getX(), event.getY())) {
+ mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -684,8 +682,7 @@
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
- // TODO: multi-display support for magnification gesture handler
- || mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
+ || mMagnificationController.isMagnifying(mDisplayId)) {
afterMultiTapTimeoutTransitionToDelegatingState();
@@ -697,8 +694,7 @@
}
break;
case ACTION_POINTER_DOWN: {
- // TODO: multi-display support for magnification gesture handler
- if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
+ if (mMagnificationController.isMagnifying(mDisplayId)) {
transitionTo(mPanningScalingState);
clear();
} else {
@@ -727,9 +723,8 @@
mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
- // TODO: multi-display support for magnification gesture handler
if (!mMagnificationController.magnificationRegionContains(
- Display.DEFAULT_DISPLAY, event.getX(), event.getY())) {
+ mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -880,8 +875,7 @@
clear();
// Toggle zoom
- // TODO: multi-display support for magnification gesture handler
- if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
+ if (mMagnificationController.isMagnifying(mDisplayId)) {
zoomOff();
} else {
zoomOn(up.getX(), up.getY());
@@ -893,9 +887,8 @@
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
clear();
- // TODO: multi-display support for magnification gesture handler
mViewportDraggingState.mZoomedInBeforeDrag =
- mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY);
+ mMagnificationController.isMagnifying(mDisplayId);
zoomOn(down.getX(), down.getY());
@@ -922,8 +915,7 @@
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
mShortcutTriggered = state;
- // TODO: multi-display support for magnification gesture handler
- mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state);
+ mMagnificationController.setForceShowMagnifiableBounds(mDisplayId, state);
}
/**
@@ -958,8 +950,7 @@
final float scale = MathUtils.constrain(
mMagnificationController.getPersistedScale(),
MIN_SCALE, MAX_SCALE);
- // TODO: multi-display support for magnification gesture handler
- mMagnificationController.setScaleAndCenter(Display.DEFAULT_DISPLAY,
+ mMagnificationController.setScaleAndCenter(mDisplayId,
scale, centerX, centerY,
/* animate */ true,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -967,8 +958,7 @@
private void zoomOff() {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
- // TODO: multi-display support for magnification gesture handler
- mMagnificationController.reset(Display.DEFAULT_DISPLAY, /* animate */ true);
+ mMagnificationController.reset(mDisplayId, /* animate */ true);
}
private static MotionEvent recycleAndNullify(@Nullable MotionEvent event) {
@@ -990,6 +980,7 @@
", mCurrentState=" + State.nameOf(mCurrentState) +
", mPreviousState=" + State.nameOf(mPreviousState) +
", mMagnificationController=" + mMagnificationController +
+ ", mDisplayId=" + mDisplayId +
'}';
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 8ffadde..65e31f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -435,7 +435,7 @@
MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
- event.getSource(), event.getFlags());
+ event.getSource(), event.getDisplayId(), event.getFlags());
final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
click_event.recycle();
@@ -1029,7 +1029,7 @@
event.getEventTime(), event.getAction(), event.getPointerCount(),
props, coords, event.getMetaState(), event.getButtonState(),
1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
- event.getSource(), event.getFlags());
+ event.getSource(), event.getDisplayId(), event.getFlags());
}
/**
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 4f58d79..303734a 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -18,6 +18,7 @@
import static com.android.server.backup.BackupManagerService.TAG;
+import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
@@ -41,9 +42,10 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import java.io.File;
@@ -75,19 +77,25 @@
* system user is unlocked before any other users.
*/
public class Trampoline extends IBackupManager.Stub {
- // When this file is present, the backup service is inactive.
+ /**
+ * Name of file that disables the backup service. If this file exists, then backup is disabled
+ * for all users.
+ */
private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+ /**
+ * Name of file for non-system users that enables the backup service for the user. Backup is
+ * disabled by default in non-system users.
+ */
+ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+
// Product-level suppression of backup/restore.
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
private static final String BACKUP_THREAD = "backup";
- /** Values for setting {@link Settings.Global#BACKUP_MULTI_USER_ENABLED} */
- private static final int MULTI_USER_DISABLED = 0;
- private static final int MULTI_USER_ENABLED = 1;
-
private final Context mContext;
+ private final UserManager mUserManager;
private final boolean mGlobalDisable;
// Lock to write backup suppress files.
@@ -104,20 +112,13 @@
mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
+ mUserManager = UserManager.get(context);
}
protected boolean isBackupDisabled() {
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
- private boolean isMultiUserEnabled() {
- return Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.BACKUP_MULTI_USER_ENABLED,
- MULTI_USER_DISABLED)
- == MULTI_USER_ENABLED;
- }
-
protected int binderGetCallingUserId() {
return Binder.getCallingUserHandle().getIdentifier();
}
@@ -126,21 +127,65 @@
return Binder.getCallingUid();
}
- protected File getSuppressFileForUser(int userId) {
- return new File(UserBackupManagerFiles.getBaseStateDir(userId),
+ /** Stored in the system user's directory. */
+ protected File getSuppressFileForSystemUser() {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
BACKUP_SUPPRESS_FILENAME);
}
- protected void createBackupSuppressFileForUser(int userId) throws IOException {
- synchronized (mStateLock) {
- getSuppressFileForUser(userId).getParentFile().mkdirs();
- getSuppressFileForUser(userId).createNewFile();
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
+ BACKUP_ACTIVATED_FILENAME + "-" + userId);
+ }
+
+ private void createFile(File file) throws IOException {
+ if (file.exists()) {
+ return;
+ }
+
+ file.getParentFile().mkdirs();
+ if (!file.createNewFile()) {
+ Slog.w(TAG, "Failed to create file " + file.getPath());
}
}
- private void deleteBackupSuppressFileForUser(int userId) {
- if (!getSuppressFileForUser(userId).delete()) {
- Slog.w(TAG, "Failed deleting backup suppressed file for user: " + userId);
+ private void deleteFile(File file) {
+ if (!file.exists()) {
+ return;
+ }
+
+ if (!file.delete()) {
+ Slog.w(TAG, "Failed to delete file " + file.getPath());
+ }
+ }
+
+ /**
+ * Deactivates the backup service for user {@code userId}. If this is the system user, it
+ * creates a suppress file which disables backup for all users. If this is a non-system user, it
+ * only deactivates backup for that user by deleting its activate file.
+ */
+ @GuardedBy("mStateLock")
+ private void deactivateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ createFile(getSuppressFileForSystemUser());
+ } else {
+ deleteFile(getActivatedFileForNonSystemUser(userId));
+ }
+ }
+
+ /**
+ * Enables the backup service for user {@code userId}. If this is the system user, it deletes
+ * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
+ * deleting the suppress file does not automatically enable backup for non-system users, they
+ * need their own activate file in order to participate in the service.
+ */
+ @GuardedBy("mStateLock")
+ private void activateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ deleteFile(getSuppressFileForSystemUser());
+ } else {
+ createFile(getActivatedFileForNonSystemUser(userId));
}
}
@@ -148,24 +193,31 @@
// admin (device owner or profile owner).
private boolean isUserReadyForBackup(int userId) {
return mService != null && mService.getServiceUsers().get(userId) != null
- && !isBackupSuppressedForUser(userId);
+ && isBackupActivatedForUser(userId);
}
- private boolean isBackupSuppressedForUser(int userId) {
- // If backup is disabled for system user, it's disabled for all other users on device.
- if (getSuppressFileForUser(UserHandle.USER_SYSTEM).exists()) {
- return true;
+ /**
+ * Backup is activated for the system user if the suppress file does not exist. Backup is
+ * activated for non-system users if the suppress file does not exist AND the user's activated
+ * file exists.
+ */
+ private boolean isBackupActivatedForUser(int userId) {
+ if (getSuppressFileForSystemUser().exists()) {
+ return false;
}
- if (userId != UserHandle.USER_SYSTEM) {
- return getSuppressFileForUser(userId).exists();
- }
- return false;
+
+ return userId == UserHandle.USER_SYSTEM
+ || getActivatedFileForNonSystemUser(userId).exists();
}
protected Context getContext() {
return mContext;
}
+ protected UserManager getUserManager() {
+ return mUserManager;
+ }
+
protected BackupManagerService createBackupManagerService() {
return new BackupManagerService(mContext, this, mHandlerThread);
}
@@ -198,23 +250,17 @@
/**
* Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
- * Starts the backup service for this user if it's the system user or if the service supports
- * multi-user. Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time
- * low.
+ * Starts the backup service for this user if backup is active for this user. Offloads work onto
+ * the handler thread {@link #mHandlerThread} to keep unlock time low.
*/
void unlockUser(int userId) {
- if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
- Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
- return;
- }
-
postToHandler(() -> startServiceForUser(userId));
}
private void startServiceForUser(int userId) {
// We know that the user is unlocked here because it is called from setBackupServiceActive
// and unlockUser which have these guarantees. So we can check if the file exists.
- if (mService != null && !isBackupSuppressedForUser(userId)) {
+ if (mService != null && isBackupActivatedForUser(userId)) {
Slog.i(TAG, "Starting service for user: " + userId);
mService.startServiceForUser(userId);
}
@@ -225,11 +271,6 @@
* Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
*/
void stopUser(int userId) {
- if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
- Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId);
- return;
- }
-
postToHandler(
() -> {
if (mService != null) {
@@ -240,45 +281,63 @@
}
/**
- * Only privileged callers should be changing the backup state. This method only acts on {@link
- * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
- * system user also deactivates backup in all users.
- *
- * This call will only work if the calling {@code userID} is unlocked.
+ * The system user and managed profiles can only be acted on by callers in the system or root
+ * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
+ * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
+ */
+ private void enforcePermissionsOnUser(int userId) throws SecurityException {
+ boolean isRestrictedUser =
+ userId == UserHandle.USER_SYSTEM
+ || getUserManager().getUserInfo(userId).isManagedProfile();
+
+ if (isRestrictedUser) {
+ int caller = binderGetCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
+ throw new SecurityException("No permission to configure backup activity");
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.BACKUP, "No permission to configure backup activity");
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "No permission to configure backup activity");
+ }
+ }
+
+ /**
+ * Only privileged callers should be changing the backup state. Deactivating backup in the
+ * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
+ * is unlocked at this point yet, so handle both cases.
*/
public void setBackupServiceActive(int userId, boolean makeActive) {
- int caller = binderGetCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
- throw new SecurityException("No permission to configure backup activity");
- }
+ enforcePermissionsOnUser(userId);
if (mGlobalDisable) {
Slog.i(TAG, "Backup service not supported");
return;
}
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.i(TAG, "Cannot set backup service activity for non-system user: " + userId);
- return;
- }
-
- if (makeActive == isBackupServiceActive(userId)) {
- Slog.i(TAG, "No change in backup service activity");
- return;
- }
-
synchronized (mStateLock) {
Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
if (makeActive) {
if (mService == null) {
mService = createBackupManagerService();
}
- deleteBackupSuppressFileForUser(userId);
- startServiceForUser(userId);
+ try {
+ activateBackupForUserLocked(userId);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service activity");
+ }
+
+ // If the user is unlocked, we can start the backup service for it. Otherwise we
+ // will start the service when the user is unlocked as part of its unlock callback.
+ if (getUserManager().isUserUnlocked(userId)) {
+ startServiceForUser(userId);
+ }
} else {
try {
//TODO(b/121198006): what if this throws an exception?
- createBackupSuppressFileForUser(userId);
+ deactivateBackupForUserLocked(userId);
} catch (IOException e) {
Slog.e(TAG, "Unable to persist backup service inactivity");
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1519c17..14e2354 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1884,12 +1884,6 @@
"ConnectivityService");
}
- private void enforceControlAlwaysOnVpnPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
- "ConnectivityService");
- }
-
private void enforceNetworkStackSettingsOrSetup() {
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
@@ -1897,12 +1891,6 @@
android.Manifest.permission.NETWORK_STACK);
}
- private void enforceNetworkStackPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.NETWORK_STACK,
- "ConnectivityService");
- }
-
private boolean checkNetworkStackPermission() {
return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
android.Manifest.permission.NETWORK_STACK);
@@ -4159,9 +4147,8 @@
}
@Override
- public boolean setAlwaysOnVpnPackage(
- int userId, String packageName, boolean lockdown, List<String> lockdownWhitelist) {
- enforceControlAlwaysOnVpnPermission();
+ public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) {
+ enforceConnectivityInternalPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
@@ -4175,11 +4162,11 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) {
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false);
return false;
}
}
@@ -4188,7 +4175,7 @@
@Override
public String getAlwaysOnVpnPackage(int userId) {
- enforceControlAlwaysOnVpnPermission();
+ enforceConnectivityInternalPermission();
enforceCrossUserPermission(userId);
synchronized (mVpns) {
@@ -4202,36 +4189,6 @@
}
@Override
- public boolean isVpnLockdownEnabled(int userId) {
- enforceControlAlwaysOnVpnPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
- return false;
- }
- return vpn.getLockdown();
- }
- }
-
- @Override
- public List<String> getVpnLockdownWhitelist(int userId) {
- enforceControlAlwaysOnVpnPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
- return null;
- }
- return vpn.getLockdownWhitelist();
- }
- }
-
- @Override
public int checkMobileProvisioning(int suggestedTimeOutMs) {
// TODO: Remove? Any reason to trigger a provisioning check?
return -1;
@@ -4460,7 +4417,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ userId);
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false);
}
}
}
@@ -6340,7 +6297,7 @@
synchronized (mVpns) {
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) {
- setAlwaysOnVpnPackage(userId, null, false, null);
+ setAlwaysOnVpnPackage(userId, null, false);
setVpnPackageAuthorization(alwaysOnPackage, userId, false);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 89ff338..c064453 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -46,9 +46,7 @@
import android.app.ActivityManager;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.InetAddresses;
import android.net.INetd;
-import android.net.INetdUnsolicitedEventListener;
import android.net.INetworkManagementEventObserver;
import android.net.ITetheringStatsProvider;
import android.net.InterfaceConfiguration;
@@ -63,6 +61,7 @@
import android.net.TetherStatsParcel;
import android.net.UidRange;
import android.net.shared.NetdService;
+import android.net.shared.NetworkObserverRegistry;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
@@ -207,16 +206,13 @@
private INetd mNetdService;
- private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener;
+ private NMSNetworkObserverRegistry mNetworkObserverRegistry;
private IBatteryStats mBatteryStats;
private final Thread mThread;
private CountDownLatch mConnectedSignal = new CountDownLatch(1);
- private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
- new RemoteCallbackList<>();
-
private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
@GuardedBy("mTetheringStatsProviders")
@@ -326,8 +322,6 @@
mDaemonHandler = new Handler(FgThread.get().getLooper());
- mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
-
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -346,7 +340,7 @@
mFgHandler = null;
mThread = null;
mServices = null;
- mNetdUnsolicitedEventListener = null;
+ mNetworkObserverRegistry = null;
}
static NetworkManagementService create(Context context, String socket, SystemServices services)
@@ -394,14 +388,12 @@
@Override
public void registerObserver(INetworkManagementEventObserver observer) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- mObservers.register(observer);
+ mNetworkObserverRegistry.registerObserver(observer);
}
@Override
public void unregisterObserver(INetworkManagementEventObserver observer) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- mObservers.unregister(observer);
+ mNetworkObserverRegistry.unregisterObserver(observer);
}
@FunctionalInterface
@@ -409,127 +401,101 @@
public void sendCallback(INetworkManagementEventObserver o) throws RemoteException;
}
- private void invokeForAllObservers(NetworkManagementEventCallback eventCallback) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- eventCallback.sendCallback(mObservers.getBroadcastItem(i));
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
+ private class NMSNetworkObserverRegistry extends NetworkObserverRegistry {
+ NMSNetworkObserverRegistry(Context context, Handler handler, INetd netd)
+ throws RemoteException {
+ super(context, handler, netd);
}
- }
- /**
- * Notify our observers of an interface status change
- */
- private void notifyInterfaceStatusChanged(String iface, boolean up) {
- invokeForAllObservers(o -> o.interfaceStatusChanged(iface, up));
- }
+ /**
+ * Notify our observers of a change in the data activity state of the interface
+ */
+ @Override
+ public void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
+ int uid, boolean fromRadio) {
+ final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type);
+ int powerState = isActive
+ ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+ : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
- /**
- * Notify our observers of an interface link state change
- * (typically, an Ethernet cable has been plugged-in or unplugged).
- */
- private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
- invokeForAllObservers(o -> o.interfaceLinkStateChanged(iface, up));
- }
-
- /**
- * Notify our observers of an interface addition.
- */
- private void notifyInterfaceAdded(String iface) {
- invokeForAllObservers(o -> o.interfaceAdded(iface));
- }
-
- /**
- * Notify our observers of an interface removal.
- */
- private void notifyInterfaceRemoved(String iface) {
- // netd already clears out quota and alerts for removed ifaces; update
- // our sanity-checking state.
- mActiveAlerts.remove(iface);
- mActiveQuotas.remove(iface);
- invokeForAllObservers(o -> o.interfaceRemoved(iface));
- }
-
- /**
- * Notify our observers of a limit reached.
- */
- private void notifyLimitReached(String limitName, String iface) {
- invokeForAllObservers(o -> o.limitReached(limitName, iface));
- }
-
- /**
- * Notify our observers of a change in the data activity state of the interface
- */
- private void notifyInterfaceClassActivity(int type, int powerState, long tsNanos,
- int uid, boolean fromRadio) {
- final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type);
- if (isMobile) {
- if (!fromRadio) {
- if (mMobileActivityFromRadio) {
- // If this call is not coming from a report from the radio itself, but we
- // have previously received reports from the radio, then we will take the
- // power state to just be whatever the radio last reported.
- powerState = mLastPowerStateFromRadio;
+ if (isMobile) {
+ if (!fromRadio) {
+ if (mMobileActivityFromRadio) {
+ // If this call is not coming from a report from the radio itself, but we
+ // have previously received reports from the radio, then we will take the
+ // power state to just be whatever the radio last reported.
+ powerState = mLastPowerStateFromRadio;
+ }
+ } else {
+ mMobileActivityFromRadio = true;
}
- } else {
- mMobileActivityFromRadio = true;
- }
- if (mLastPowerStateFromRadio != powerState) {
- mLastPowerStateFromRadio = powerState;
- try {
- getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
- } catch (RemoteException e) {
+ if (mLastPowerStateFromRadio != powerState) {
+ mLastPowerStateFromRadio = powerState;
+ try {
+ getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
+ } catch (RemoteException e) {
+ }
}
StatsLog.write_non_chained(StatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null,
powerState);
}
- }
- if (ConnectivityManager.isNetworkTypeWifi(type)) {
- if (mLastPowerStateFromWifi != powerState) {
- mLastPowerStateFromWifi = powerState;
- try {
- getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
- } catch (RemoteException e) {
+ if (ConnectivityManager.isNetworkTypeWifi(type)) {
+ if (mLastPowerStateFromWifi != powerState) {
+ mLastPowerStateFromWifi = powerState;
+ try {
+ getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
+ } catch (RemoteException e) {
+ }
}
StatsLog.write_non_chained(StatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null,
powerState);
}
- }
- boolean isActive = powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
- || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
-
- if (!isMobile || fromRadio || !mMobileActivityFromRadio) {
- // Report the change in data activity. We don't do this if this is a change
- // on the mobile network, that is not coming from the radio itself, and we
- // have previously seen change reports from the radio. In that case only
- // the radio is the authority for the current state.
- final boolean active = isActive;
- invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
- Integer.toString(type), active, tsNanos));
- }
-
- boolean report = false;
- synchronized (mIdleTimerLock) {
- if (mActiveIdleTimers.isEmpty()) {
- // If there are no idle timers, we are not monitoring activity, so we
- // are always considered active.
- isActive = true;
+ if (!isMobile || fromRadio || !mMobileActivityFromRadio) {
+ // Report the change in data activity. We don't do this if this is a change
+ // on the mobile network, that is not coming from the radio itself, and we
+ // have previously seen change reports from the radio. In that case only
+ // the radio is the authority for the current state.
+ final boolean active = isActive;
+ super.notifyInterfaceClassActivity(type, isActive, tsNanos, uid, fromRadio);
}
- if (mNetworkActive != isActive) {
- mNetworkActive = isActive;
- report = isActive;
+
+ boolean report = false;
+ synchronized (mIdleTimerLock) {
+ if (mActiveIdleTimers.isEmpty()) {
+ // If there are no idle timers, we are not monitoring activity, so we
+ // are always considered active.
+ isActive = true;
+ }
+ if (mNetworkActive != isActive) {
+ mNetworkActive = isActive;
+ report = isActive;
+ }
+ }
+ if (report) {
+ reportNetworkActive();
}
}
- if (report) {
- reportNetworkActive();
+
+ /**
+ * Notify our observers of an interface removal.
+ */
+ @Override
+ public void notifyInterfaceRemoved(String iface) {
+ // netd already clears out quota and alerts for removed ifaces; update
+ // our sanity-checking state.
+ mActiveAlerts.remove(iface);
+ mActiveQuotas.remove(iface);
+ super.notifyInterfaceRemoved(iface);
+ }
+
+ @Override
+ public void onStrictCleartextDetected(int uid, String hex) throws RemoteException {
+ // Don't need to post to mDaemonHandler because the only thing
+ // that notifyCleartextNetwork does is post to a handler
+ ActivityManager.getService().notifyCleartextNetwork(uid,
+ HexDump.hexStringToByteArray(hex));
}
}
@@ -558,7 +524,8 @@
return;
}
// No current code examines the interface parameter in a global alert. Just pass null.
- mDaemonHandler.post(() -> notifyLimitReached(LIMIT_GLOBAL_ALERT, null));
+ mDaemonHandler.post(() -> mNetworkObserverRegistry.notifyLimitReached(
+ LIMIT_GLOBAL_ALERT, null));
}
}
@@ -590,10 +557,11 @@
private void connectNativeNetdService() {
mNetdService = mServices.getNetd();
try {
- mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
- if (DBG) Slog.d(TAG, "Register unsolicited event listener");
+ mNetworkObserverRegistry = new NMSNetworkObserverRegistry(
+ mContext, mDaemonHandler, mNetdService);
+ if (DBG) Slog.d(TAG, "Registered NetworkObserverRegistry");
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);
+ Slog.wtf(TAG, "Failed to register NetworkObserverRegistry: " + e);
}
}
@@ -697,120 +665,6 @@
}
- /**
- * Notify our observers of a new or updated interface address.
- */
- private void notifyAddressUpdated(String iface, LinkAddress address) {
- invokeForAllObservers(o -> o.addressUpdated(iface, address));
- }
-
- /**
- * Notify our observers of a deleted interface address.
- */
- private void notifyAddressRemoved(String iface, LinkAddress address) {
- invokeForAllObservers(o -> o.addressRemoved(iface, address));
- }
-
- /**
- * Notify our observers of DNS server information received.
- */
- private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
- invokeForAllObservers(o -> o.interfaceDnsServerInfo(iface, lifetime, addresses));
- }
-
- /**
- * Notify our observers of a route change.
- */
- private void notifyRouteChange(boolean updated, RouteInfo route) {
- if (updated) {
- invokeForAllObservers(o -> o.routeUpdated(route));
- } else {
- invokeForAllObservers(o -> o.routeRemoved(route));
- }
- }
-
- private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
- @Override
- public void onInterfaceClassActivityChanged(boolean isActive,
- int label, long timestamp, int uid) throws RemoteException {
- final long timestampNanos;
- if (timestamp <= 0) {
- timestampNanos = SystemClock.elapsedRealtimeNanos();
- } else {
- timestampNanos = timestamp;
- }
- mDaemonHandler.post(() -> notifyInterfaceClassActivity(label,
- isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
- : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
- timestampNanos, uid, false));
- }
-
- @Override
- public void onQuotaLimitReached(String alertName, String ifName)
- throws RemoteException {
- mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName));
- }
-
- @Override
- public void onInterfaceDnsServerInfo(String ifName,
- long lifetime, String[] servers) throws RemoteException {
- mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers));
- }
-
- @Override
- public void onInterfaceAddressUpdated(String addr,
- String ifName, int flags, int scope) throws RemoteException {
- final LinkAddress address = new LinkAddress(addr, flags, scope);
- mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address));
- }
-
- @Override
- public void onInterfaceAddressRemoved(String addr,
- String ifName, int flags, int scope) throws RemoteException {
- final LinkAddress address = new LinkAddress(addr, flags, scope);
- mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address));
- }
-
- @Override
- public void onInterfaceAdded(String ifName) throws RemoteException {
- mDaemonHandler.post(() -> notifyInterfaceAdded(ifName));
- }
-
- @Override
- public void onInterfaceRemoved(String ifName) throws RemoteException {
- mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName));
- }
-
- @Override
- public void onInterfaceChanged(String ifName, boolean up)
- throws RemoteException {
- mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));
- }
-
- @Override
- public void onInterfaceLinkStateChanged(String ifName, boolean up)
- throws RemoteException {
- mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));
- }
-
- @Override
- public void onRouteChanged(boolean updated,
- String route, String gateway, String ifName) throws RemoteException {
- final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
- ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
- ifName);
- mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute));
- }
-
- @Override
- public void onStrictCleartextDetected(int uid, String hex) throws RemoteException {
- // Don't need to post to mDaemonHandler because the only thing
- // that notifyCleartextNetwork does is post to a handler
- ActivityManager.getService().notifyCleartextNetwork(uid,
- HexDump.hexStringToByteArray(hex));
- }
- }
-
//
// Netd Callback handling
//
@@ -859,16 +713,18 @@
throw new IllegalStateException(errorMessage);
}
if (cooked[2].equals("added")) {
- notifyInterfaceAdded(cooked[3]);
+ mNetworkObserverRegistry.notifyInterfaceAdded(cooked[3]);
return true;
} else if (cooked[2].equals("removed")) {
- notifyInterfaceRemoved(cooked[3]);
+ mNetworkObserverRegistry.notifyInterfaceRemoved(cooked[3]);
return true;
} else if (cooked[2].equals("changed") && cooked.length == 5) {
- notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
+ mNetworkObserverRegistry.notifyInterfaceStatusChanged(
+ cooked[3], cooked[4].equals("up"));
return true;
} else if (cooked[2].equals("linkstate") && cooked.length == 5) {
- notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
+ mNetworkObserverRegistry.notifyInterfaceLinkStateChanged(
+ cooked[3], cooked[4].equals("up"));
return true;
}
throw new IllegalStateException(errorMessage);
@@ -882,7 +738,7 @@
throw new IllegalStateException(errorMessage);
}
if (cooked[2].equals("alert")) {
- notifyLimitReached(cooked[3], cooked[4]);
+ mNetworkObserverRegistry.notifyLimitReached(cooked[3], cooked[4]);
return true;
}
throw new IllegalStateException(errorMessage);
@@ -908,9 +764,8 @@
timestampNanos = SystemClock.elapsedRealtimeNanos();
}
boolean isActive = cooked[2].equals("active");
- notifyInterfaceClassActivity(Integer.parseInt(cooked[3]),
- isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
- : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ mNetworkObserverRegistry.notifyInterfaceClassActivity(
+ Integer.parseInt(cooked[3]), isActive,
timestampNanos, processUid, false);
return true;
// break;
@@ -937,9 +792,9 @@
}
if (cooked[2].equals("updated")) {
- notifyAddressUpdated(iface, address);
+ mNetworkObserverRegistry.notifyAddressUpdated(iface, address);
} else {
- notifyAddressRemoved(iface, address);
+ mNetworkObserverRegistry.notifyAddressRemoved(iface, address);
}
return true;
// break;
@@ -959,7 +814,8 @@
throw new IllegalStateException(errorMessage);
}
String[] servers = cooked[5].split(",");
- notifyInterfaceDnsServerInfo(cooked[3], lifetime, servers);
+ mNetworkObserverRegistry.notifyInterfaceDnsServerInfo(
+ cooked[3], lifetime, servers);
}
return true;
// break;
@@ -998,7 +854,8 @@
InetAddress gateway = null;
if (via != null) gateway = InetAddress.parseNumericAddress(via);
RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
- notifyRouteChange(cooked[2].equals("updated"), route);
+ mNetworkObserverRegistry.notifyRouteChange(
+ cooked[2].equals("updated"), route);
return true;
} catch (IllegalArgumentException e) {}
}
@@ -1461,9 +1318,8 @@
if (ConnectivityManager.isNetworkTypeMobile(type)) {
mNetworkActive = false;
}
- mDaemonHandler.post(() -> notifyInterfaceClassActivity(type,
- DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
- SystemClock.elapsedRealtimeNanos(), -1, false));
+ mDaemonHandler.post(() -> mNetworkObserverRegistry.notifyInterfaceClassActivity(
+ type, true /* isActive */, SystemClock.elapsedRealtimeNanos(), -1, false));
}
}
@@ -1486,9 +1342,9 @@
throw new IllegalStateException(e);
}
mActiveIdleTimers.remove(iface);
- mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type,
- DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
- SystemClock.elapsedRealtimeNanos(), -1, false));
+ mDaemonHandler.post(() -> mNetworkObserverRegistry.notifyInterfaceClassActivity(
+ params.type, false /* isActive */, SystemClock.elapsedRealtimeNanos(), -1,
+ false));
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 9d810cd..cec825f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -112,6 +112,7 @@
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.sysprop.VoldProperties;
@@ -463,6 +464,7 @@
= { "password", "default", "pattern", "pin" };
private final Context mContext;
+ private final ContentResolver mResolver;
private volatile IVold mVold;
private volatile IStoraged mStoraged;
@@ -797,6 +799,14 @@
refreshIsolatedStorageSettings();
}
});
+ // For now, simply clone property when it changes
+ DeviceConfig.addOnPropertyChangedListener(DeviceConfig.Storage.NAMESPACE,
+ mContext.getMainExecutor(), (namespace, name, value) -> {
+ if (DeviceConfig.Storage.ISOLATED_STORAGE_ENABLED.equals(name)) {
+ Settings.Global.putString(mResolver,
+ Settings.Global.ISOLATED_STORAGE_REMOTE, value);
+ }
+ });
refreshIsolatedStorageSettings();
}
@@ -1523,6 +1533,8 @@
SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)));
mContext = context;
+ mResolver = mContext.getContentResolver();
+
mCallbacks = new Callbacks(FgThread.get().getLooper());
mLockPatternUtils = new LockPatternUtils(mContext);
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index aabb587..dc9a4bf 100644
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import com.android.internal.R;
-
import android.app.AlertDialog;
import android.content.Context;
import android.os.Handler;
@@ -26,6 +24,8 @@
import android.view.WindowManager;
import android.widget.Button;
+import com.android.internal.R;
+
public class BaseErrorDialog extends AlertDialog {
private static final int ENABLE_BUTTONS = 0;
private static final int DISABLE_BUTTONS = 1;
@@ -36,7 +36,7 @@
super(context, com.android.internal.R.style.Theme_DeviceDefault_Dialog_AppError);
context.assertRuntimeOverlayThemable();
- getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index d3953b5..3d69aa8 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -61,6 +61,8 @@
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
sGlobalSettingToTypeMap.put(
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 55cca95..0b27a8a 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -379,6 +379,10 @@
if (userId == UserHandle.USER_CURRENT) {
userId = controller.mUserController.getCurrentOrTargetUserId();
}
+ // temporarily allow receivers and services to open activities from background if the
+ // PendingIntent.send() caller was foreground at the time of sendInner() call
+ final boolean allowTrampoline = uid != callingUid
+ && controller.mAtmInternal.isUidForeground(callingUid);
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
@@ -419,7 +423,8 @@
uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
requiredPermission, options, (finishedReceiver != null),
false, userId,
- mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken));
+ mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
+ || allowTrampoline);
if (sent == ActivityManager.BROADCAST_SUCCESS) {
sendFinish = false;
}
@@ -433,7 +438,8 @@
controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
key.packageName, userId,
- mAllowBgActivityStartsForServiceSender.contains(whitelistToken));
+ mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
+ || allowTrampoline);
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startService intent", e);
} catch (TransactionTooLargeException e) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2508844..62a1b03 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -151,7 +151,7 @@
.divide(BigInteger.valueOf(100));
}
// How many routes to evaluate before bailing and declaring this Vpn should provide
- // the INTERNET capability. This is necessary because computing the address space is
+ // the INTERNET capability. This is necessary because computing the adress space is
// O(n²) and this is running in the system service, so a limit is needed to alleviate
// the risk of attack.
// This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
@@ -194,12 +194,6 @@
private boolean mLockdown = false;
/**
- * Set of packages in addition to the VPN app itself that can access the network directly when
- * VPN is not connected even if {@code mLockdown} is set.
- */
- private @NonNull List<String> mLockdownWhitelist = Collections.emptyList();
-
- /**
* List of UIDs for which networking should be blocked until VPN is ready, during brief periods
* when VPN is not running. For example, during system startup or after a crash.
* @see mLockdown
@@ -326,9 +320,9 @@
*
* Used to enable/disable legacy VPN lockdown.
*
- * This uses the same ip rule mechanism as
- * {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling
- * that function will be replaced and saved with the always-on state.
+ * This uses the same ip rule mechanism as {@link #setAlwaysOnPackage(String, boolean)};
+ * previous settings from calling that function will be replaced and saved with the
+ * always-on state.
*
* @param lockdown whether to prevent all traffic outside of a VPN.
*/
@@ -425,14 +419,12 @@
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
- * @param lockdownWhitelist packages to be whitelisted from lockdown.
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
- public synchronized boolean setAlwaysOnPackage(
- String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) {
enforceControlPermissionOrInternalCaller();
- if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist)) {
+ if (setAlwaysOnPackageInternal(packageName, lockdown)) {
saveAlwaysOnPackage();
return true;
}
@@ -447,27 +439,15 @@
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
- * @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if
- * {@code lockdown} is {@code true}. Packages must not contain commas.
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
@GuardedBy("this")
- private boolean setAlwaysOnPackageInternal(
- String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ private boolean setAlwaysOnPackageInternal(String packageName, boolean lockdown) {
if (VpnConfig.LEGACY_VPN.equals(packageName)) {
Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
return false;
}
- if (lockdownWhitelist != null) {
- for (String pkg : lockdownWhitelist) {
- if (pkg.contains(",")) {
- Log.w(TAG, "Not setting always-on vpn, invalid whitelisted package: " + pkg);
- return false;
- }
- }
- }
-
if (packageName != null) {
// Pre-authorize new always-on VPN package.
if (!setPackageAuthorization(packageName, true)) {
@@ -480,18 +460,13 @@
}
mLockdown = (mAlwaysOn && lockdown);
- mLockdownWhitelist = (mLockdown && lockdownWhitelist != null)
- ? Collections.unmodifiableList(new ArrayList<>(lockdownWhitelist))
- : Collections.emptyList();
-
if (isCurrentPreparedPackage(packageName)) {
updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
- setVpnForcedLocked(mLockdown);
} else {
// Prepare this app. The notification will update as a side-effect of updateState().
- // It also calls setVpnForcedLocked().
prepareInternal(packageName);
}
+ setVpnForcedLocked(mLockdown);
return true;
}
@@ -503,6 +478,7 @@
* @return the package name of the VPN controller responsible for always-on VPN,
* or {@code null} if none is set or always-on VPN is controlled through
* lockdown instead.
+ * @hide
*/
public synchronized String getAlwaysOnPackage() {
enforceControlPermissionOrInternalCaller();
@@ -510,13 +486,6 @@
}
/**
- * @return an immutable list of packages whitelisted from always-on VPN lockdown.
- */
- public synchronized List<String> getLockdownWhitelist() {
- return mLockdown ? mLockdownWhitelist : null;
- }
-
- /**
* Save the always-on package and lockdown config into Settings.Secure
*/
@GuardedBy("this")
@@ -527,9 +496,6 @@
getAlwaysOnPackage(), mUserHandle);
mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
(mAlwaysOn && mLockdown ? 1 : 0), mUserHandle);
- mSystemServices.settingsSecurePutStringForUser(
- Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST,
- String.join(",", mLockdownWhitelist), mUserHandle);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -546,11 +512,7 @@
Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser(
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0;
- final String whitelistString = mSystemServices.settingsSecureGetStringForUser(
- Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle);
- final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString)
- ? Collections.emptyList() : Arrays.asList(whitelistString.split(","));
- setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown, whitelistedPackages);
+ setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -570,7 +532,7 @@
}
// Remove always-on VPN if it's not supported.
if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
- setAlwaysOnPackage(null, false, null);
+ setAlwaysOnPackage(null, false);
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
@@ -1287,10 +1249,9 @@
}
/**
- * Restricts network access from all UIDs affected by this {@link Vpn}, apart from the VPN
- * service app itself and whitelisted packages, to only sockets that have had {@code protect()}
- * called on them. All non-VPN traffic is blocked via a {@code PROHIBIT} response from the
- * kernel.
+ * Restrict network access from all UIDs affected by this {@link Vpn}, apart from the VPN
+ * service app itself, to only sockets that have had {@code protect()} called on them. All
+ * non-VPN traffic is blocked via a {@code PROHIBIT} response from the kernel.
*
* The exception for the VPN UID isn't technically necessary -- setup should use protected
* sockets -- but in practice it saves apps that don't protect their sockets from breaking.
@@ -1306,13 +1267,8 @@
*/
@GuardedBy("this")
private void setVpnForcedLocked(boolean enforce) {
- final List<String> exemptedPackages;
- if (isNullOrLegacyVpn(mPackage)) {
- exemptedPackages = null;
- } else {
- exemptedPackages = new ArrayList<>(mLockdownWhitelist);
- exemptedPackages.add(mPackage);
- }
+ final List<String> exemptedPackages =
+ isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage);
final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
Set<UidRange> addedRanges = Collections.emptySet();
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 5ed6263..e268e44 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -48,7 +48,6 @@
import android.os.Bundle;
import android.os.FactoryTest;
import android.os.IBinder;
-import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -255,21 +254,6 @@
}
}
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (RuntimeException e) {
- // The content service only throws security exceptions, so let's
- // log all others.
- if (!(e instanceof SecurityException)) {
- Slog.wtf(TAG, "Content Service Crash", e);
- }
- throw e;
- }
- }
-
/*package*/ ContentService(Context context, boolean factoryTest) {
mContext = context;
mFactoryTest = factoryTest;
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 58c88b3..eb0ed0a 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -19,7 +19,10 @@
import static android.hardware.display.ColorDisplayManager.AUTO_MODE_CUSTOM_TIME;
import static android.hardware.display.ColorDisplayManager.AUTO_MODE_DISABLED;
import static android.hardware.display.ColorDisplayManager.AUTO_MODE_TWILIGHT;
-
+import static android.hardware.display.ColorDisplayManager.COLOR_MODE_AUTOMATIC;
+import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
+import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
+import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
@@ -45,6 +48,7 @@
import android.graphics.ColorSpace;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.ColorDisplayManager.AutoMode;
+import android.hardware.display.ColorDisplayManager.ColorMode;
import android.hardware.display.IColorDisplayManager;
import android.hardware.display.Time;
import android.net.Uri;
@@ -53,6 +57,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.provider.Settings.System;
@@ -61,7 +66,6 @@
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
-
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
@@ -71,7 +75,6 @@
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -162,7 +165,7 @@
}
float[] displayRedGreenBlueXYZ =
- new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+ new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
@@ -173,10 +176,10 @@
}
final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
- "Display Color Space",
- displayRedGreenBlueXYZ,
- displayWhiteXYZ,
- 2.2f // gamma, unused for display white balance
+ "Display Color Space",
+ displayRedGreenBlueXYZ,
+ displayWhiteXYZ,
+ 2.2f // gamma, unused for display white balance
);
float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
@@ -242,8 +245,8 @@
mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
mChromaticAdaptationMatrix =
- ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
- mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
+ ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+ mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
// Convert the adaptation matrix to RGB space
float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
@@ -385,7 +388,7 @@
* subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
* ProgramCache for full implementation details.
*/
- private static final float[] MATRIX_INVERT_COLOR = new float[] {
+ private static final float[] MATRIX_INVERT_COLOR = new float[]{
0.402f, -0.598f, -0.599f, 0f,
-1.174f, -0.174f, -1.175f, 0f,
-0.228f, -0.228f, 0.772f, 0f,
@@ -944,6 +947,89 @@
.setSaturationLevel(packageName, mCurrentUser, saturationLevel);
}
+ private void setColorModeInternal(@ColorMode int colorMode) {
+ if (!isColorModeAvailable(colorMode)) {
+ throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
+ }
+ System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE,
+ colorMode,
+ mCurrentUser);
+ }
+
+ private @ColorMode
+ int getColorModeInternal() {
+ final ContentResolver cr = getContext().getContentResolver();
+ if (Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+ 0, mCurrentUser) == 1
+ || Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+ 0, mCurrentUser) == 1) {
+ // There are restrictions on the available color modes combined with a11y transforms.
+ if (isColorModeAvailable(COLOR_MODE_SATURATED)) {
+ return COLOR_MODE_SATURATED;
+ } else if (isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
+ return COLOR_MODE_AUTOMATIC;
+ }
+ }
+
+ int colorMode = System.getIntForUser(cr, System.DISPLAY_COLOR_MODE, -1, mCurrentUser);
+ if (colorMode == -1) {
+ // There might be a system property controlling color mode that we need to respect; if
+ // not, this will set a suitable default.
+ colorMode = getCurrentColorModeFromSystemProperties();
+ }
+
+ // This happens when a color mode is no longer available (e.g., after system update or B&R)
+ // or the device does not support any color mode.
+ if (!isColorModeAvailable(colorMode)) {
+ if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
+ colorMode = COLOR_MODE_NATURAL;
+ } else if (colorMode == COLOR_MODE_SATURATED
+ && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
+ colorMode = COLOR_MODE_AUTOMATIC;
+ } else if (colorMode == COLOR_MODE_AUTOMATIC
+ && isColorModeAvailable(COLOR_MODE_SATURATED)) {
+ colorMode = COLOR_MODE_SATURATED;
+ } else {
+ colorMode = -1;
+ }
+ }
+
+ return colorMode;
+ }
+
+ /**
+ * Get the current color mode from system properties, or return -1 if invalid.
+ *
+ * See {@link com.android.server.display.DisplayTransformManager}
+ */
+ private @ColorMode
+ int getCurrentColorModeFromSystemProperties() {
+ final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
+ if (displayColorSetting == 0) {
+ return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
+ ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
+ } else if (displayColorSetting == 1) {
+ return COLOR_MODE_SATURATED;
+ } else if (displayColorSetting == 2) {
+ return COLOR_MODE_AUTOMATIC;
+ } else {
+ return -1;
+ }
+ }
+
+ private boolean isColorModeAvailable(@ColorMode int colorMode) {
+ final int[] availableColorModes = getContext().getResources().getIntArray(
+ R.array.config_availableColorModes);
+ if (availableColorModes != null) {
+ for (int mode : availableColorModes) {
+ if (mode == colorMode) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
pw.println("Night Display:");
@@ -1445,7 +1531,9 @@
*/
public interface ColorTransformController {
- /** Apply the given saturation (grayscale) matrix to the associated AppWindow. */
+ /**
+ * Apply the given saturation (grayscale) matrix to the associated AppWindow.
+ */
void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
}
@@ -1453,6 +1541,29 @@
final class BinderService extends IColorDisplayManager.Stub {
@Override
+ public void setColorMode(int colorMode) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set display color mode");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setColorModeInternal(colorMode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getColorMode() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getColorModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public boolean isDeviceColorManaged() {
final long token = Binder.clearCallingIdentity();
try {
@@ -1640,7 +1751,9 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+ return;
+ }
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index a5e9728..b1b7d3c 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.app.ActivityTaskManager;
+import android.hardware.display.ColorDisplayManager;
import android.opengl.Matrix;
import android.os.IBinder;
import android.os.Parcel;
@@ -27,7 +28,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.ColorDisplayController;
import java.util.Arrays;
@@ -248,20 +248,20 @@
* work in linear space.
*/
public static boolean needsLinearColorMatrix(int colorMode) {
- return colorMode != ColorDisplayController.COLOR_MODE_SATURATED;
+ return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED;
}
public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) {
- if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
+ if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) {
applySaturation(COLOR_SATURATION_NATURAL);
setDisplayColor(DISPLAY_COLOR_MANAGED);
- } else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
+ } else if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) {
applySaturation(COLOR_SATURATION_BOOSTED);
setDisplayColor(DISPLAY_COLOR_MANAGED);
- } else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
+ } else if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) {
applySaturation(COLOR_SATURATION_NATURAL);
setDisplayColor(DISPLAY_COLOR_UNMANAGED);
- } else if (colorMode == ColorDisplayController.COLOR_MODE_AUTOMATIC) {
+ } else if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) {
applySaturation(COLOR_SATURATION_NATURAL);
setDisplayColor(DISPLAY_COLOR_ENHANCED);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3fd3945..3b4e4dd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -288,6 +288,8 @@
private static final class DebugFlags {
static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
new DebugFlag("debug.optimize_startinput", false);
+ static final DebugFlag FLAG_PRE_RENDER_IME_VIEWS =
+ new DebugFlag("persist.pre_render_ime_views", false);
}
@UserIdInt
@@ -304,6 +306,7 @@
final boolean mHasFeature;
private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
new ArrayMap<>();
+ private final boolean mIsLowRam;
private final HardKeyboardListener mHardKeyboardListener;
private final AppOpsManager mAppOpsManager;
private final UserManager mUserManager;
@@ -403,6 +406,10 @@
final ClientDeathRecipient clientDeathRecipient;
boolean sessionRequested;
+ // Determines if IMEs should be pre-rendered.
+ // DebugFlag can be flipped anytime. This flag is kept per-client to maintain behavior
+ // through the life of the current client.
+ boolean shouldPreRenderIme;
SessionState curSession;
@Override
@@ -615,6 +622,10 @@
* <dd>
* If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
* </dd>
+ * dt>{@link InputMethodService#IME_INVISIBLE}</dt>
+ * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
+ * currently invisible.
+ * </dd>
* </dl>
* <em>Do not update this value outside of setImeWindowStatus.</em>
*/
@@ -1361,6 +1372,7 @@
mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
mHardKeyboardBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_externalHardKeyboardBehavior);
+ mIsLowRam = ActivityManager.isLowRamDeviceStatic();
Bundle extras = new Bundle();
extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
@@ -2264,7 +2276,10 @@
if (mSwitchingDialog != null) return false;
if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
&& mKeyguardManager != null && mKeyguardManager.isKeyguardSecure()) return false;
- if ((visibility & InputMethodService.IME_ACTIVE) == 0) return false;
+ if ((visibility & InputMethodService.IME_ACTIVE) == 0
+ || (visibility & InputMethodService.IME_INVISIBLE) != 0) {
+ return false;
+ }
if (mWindowManagerInternal.isHardKeyboardAvailable()) {
if (mHardKeyboardBehavior == HardKeyboardBehavior.WIRELESS_AFFORDANCE) {
// When physical keyboard is attached, we show the ime switcher (or notification if
@@ -2372,6 +2387,12 @@
if (mCurToken == null) {
return;
}
+ if (DEBUG) {
+ Slog.d(TAG, "IME window vis: " + vis
+ + " active: " + (vis & InputMethodService.IME_ACTIVE)
+ + " inv: " + (vis & InputMethodService.IME_INVISIBLE));
+ }
+
// TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
// all updateSystemUi happens on system previlege.
final long ident = Binder.clearCallingIdentity();
@@ -2830,6 +2851,14 @@
if (PER_PROFILE_IME_ENABLED && userId != mSettings.getCurrentUserId()) {
switchUserLocked(userId);
}
+ // Master feature flag that overrides other conditions and forces IME preRendering.
+ if (DEBUG) {
+ Slog.v(TAG, "IME PreRendering MASTER flag: "
+ + DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value()
+ + ", LowRam: " + mIsLowRam);
+ }
+ // pre-rendering not supported on low-ram devices.
+ cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam;
if (mCurFocusedWindow == windowToken) {
if (DEBUG) {
@@ -3493,7 +3522,7 @@
try {
setEnabledSessionInMainThread(session);
session.method.startInput(startInputToken, inputContext, missingMethods,
- editorInfo, restarting);
+ editorInfo, restarting, session.client.shouldPreRenderIme);
} catch (RemoteException e) {
}
args.recycle();
@@ -4421,6 +4450,7 @@
@ShellCommandResult
private int refreshDebugProperties() {
DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
+ DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.refresh();
return ShellCommandResult.SUCCESS;
}
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index 591889d..ca9c0e0 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -19,9 +19,15 @@
import android.annotation.SuppressLint;
import android.app.AppOpsManager;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -55,6 +61,8 @@
private static final String LOCATION_PERMISSION_NAME =
"android.permission.ACCESS_FINE_LOCATION";
+ private static final String[] NO_LOCATION_ENABLED_PROXY_APPS = new String[0];
+
// Wakelocks
private static final String WAKELOCK_KEY = TAG;
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
@@ -66,13 +74,16 @@
private final Handler mHandler;
private final Context mContext;
+ private boolean mIsMasterLocationSettingsEnabled = true;
+ private boolean mIsOnRoamingNetwork = false;
+
// Number of non-framework location access proxy apps is expected to be small (< 5).
private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
private HashMap<String, Boolean> mProxyAppToLocationPermissions = new HashMap<>(
HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS);
private PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener =
- uid -> postEvent(() -> handlePermissionsChanged(uid));
+ uid -> runOnHandler(() -> handlePermissionsChanged(uid));
GnssVisibilityControl(Context context, Looper looper) {
mContext = context;
@@ -81,8 +92,15 @@
mHandler = new Handler(looper);
mAppOps = mContext.getSystemService(AppOpsManager.class);
mPackageManager = mContext.getPackageManager();
+
+ // Set to empty proxy app list initially until the configuration properties are loaded.
+ updateNfwLocationAccessProxyAppsInGnssHal();
+
+ // Listen for proxy app package installation, removal events.
+ listenForProxyAppsPackageUpdates();
+ listenForRoamingNetworkUpdate();
+
// TODO(b/122855984): Handle global location settings on/off.
- // TODO(b/122856189): Handle roaming case.
}
void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
@@ -90,18 +108,68 @@
// but rather piggy backs on the GnssLocationProvider SIM_STATE_CHANGED handling
// so that the order of processing is preserved. GnssLocationProvider should
// first load the new config parameters for the new SIM and then call this method.
- postEvent(() -> handleSubscriptionOrSimChanged(nfwLocationAccessProxyApps));
+ runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
+ }
+
+ void masterLocationSettingsUpdated(boolean enabled) {
+ runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled));
}
void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
boolean inEmergencyMode, boolean isCachedLocation) {
- postEvent(() -> handleNfwNotification(
+ runOnHandler(() -> handleNfwNotification(
new NfwNotification(proxyAppPackageName, protocolStack, otherProtocolStackName,
requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
}
- private void handleSubscriptionOrSimChanged(List<String> nfwLocationAccessProxyApps) {
+ private void listenForProxyAppsPackageUpdates() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ case Intent.ACTION_PACKAGE_REMOVED:
+ case Intent.ACTION_PACKAGE_REPLACED:
+ String pkgName = intent.getData().getEncodedSchemeSpecificPart();
+ handleProxyAppPackageUpdate(pkgName, action);
+ break;
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, mHandler);
+ }
+
+ private void handleProxyAppPackageUpdate(String pkgName, String action) {
+ final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
+ // pkgName is not one of the proxy apps in our list.
+ if (locationPermission == null) {
+ return;
+ }
+
+ Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action);
+ final boolean updatedLocationPermission = hasLocationPermission(pkgName);
+ if (locationPermission != updatedLocationPermission) {
+ // Permission changed. So, update the GNSS HAL with the updated list.
+ mProxyAppToLocationPermissions.put(pkgName, updatedLocationPermission);
+ updateNfwLocationAccessProxyAppsInGnssHal();
+ }
+ }
+
+ private void handleUpdateProxyApps(List<String> nfwLocationAccessProxyApps) {
+ if (!isProxyAppListUpdated(nfwLocationAccessProxyApps)) {
+ return;
+ }
+
if (nfwLocationAccessProxyApps.isEmpty()) {
// Stop listening for app permission changes. Clear the app list in GNSS HAL.
if (!mProxyAppToLocationPermissions.isEmpty()) {
@@ -125,6 +193,27 @@
updateNfwLocationAccessProxyAppsInGnssHal();
}
+ private boolean isProxyAppListUpdated(List<String> nfwLocationAccessProxyApps) {
+ if (nfwLocationAccessProxyApps.size() != mProxyAppToLocationPermissions.size()) {
+ return true;
+ }
+
+ for (String nfwLocationAccessProxyApp : nfwLocationAccessProxyApps) {
+ if (!mProxyAppToLocationPermissions.containsKey(nfwLocationAccessProxyApp)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void handleMasterLocationSettingsUpdated(boolean enabled) {
+ mIsMasterLocationSettingsEnabled = enabled;
+ Log.i(TAG, "Master location settings switch changed to "
+ + (enabled ? "enabled" : "disabled"));
+ updateNfwLocationAccessProxyAppsInGnssHal();
+ }
+
// Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
private static class NfwNotification {
private static final String KEY_PROTOCOL_STACK = "ProtocolStack";
@@ -149,7 +238,7 @@
private final boolean mInEmergencyMode;
private final boolean mIsCachedLocation;
- NfwNotification(String proxyAppPackageName, byte protocolStack,
+ private NfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId,
byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
mProxyAppPackageName = proxyAppPackageName;
@@ -162,7 +251,7 @@
mIsCachedLocation = isCachedLocation;
}
- void copyFieldsToIntent(Intent intent) {
+ private void copyFieldsToIntent(Intent intent) {
intent.putExtra(KEY_PROTOCOL_STACK, mProtocolStack);
if (!TextUtils.isEmpty(mOtherProtocolStackName)) {
intent.putExtra(KEY_OTHER_PROTOCOL_STACK_NAME, mOtherProtocolStackName);
@@ -188,7 +277,7 @@
mRequestor, mRequestorId, mResponseType, mInEmergencyMode, mIsCachedLocation);
}
- String getResponseTypeAsString() {
+ private String getResponseTypeAsString() {
switch (mResponseType) {
case NFW_RESPONSE_TYPE_REJECTED:
return "REJECTED";
@@ -246,6 +335,24 @@
}
private void updateNfwLocationAccessProxyAppsInGnssHal() {
+ final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess()
+ ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps();
+ final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
+ Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
+ + proxyAppsStr);
+ boolean result = native_enable_nfw_location_access(locationPermissionEnabledProxyApps);
+ if (!result) {
+ Log.e(TAG, "Failed to update non-framework location access proxy apps in the"
+ + " GNSS HAL to: " + proxyAppsStr);
+ }
+ }
+
+ private boolean shouldDisableNfwLocationAccess() {
+ // TODO(b/122856189): Add disableWhenRoaming configuration per proxy app.
+ return mIsOnRoamingNetwork || !mIsMasterLocationSettingsEnabled;
+ }
+
+ private String[] getLocationPermissionEnabledProxyApps() {
// Get a count of proxy apps with location permission enabled to array creation size.
int countLocationPermissionEnabledProxyApps = 0;
for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
@@ -264,15 +371,7 @@
locationPermissionEnabledProxyApps[i++] = proxyApp;
}
}
-
- String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
- Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
- + proxyAppsStr);
- boolean result = native_enable_nfw_location_access(locationPermissionEnabledProxyApps);
- if (!result) {
- Log.e(TAG, "Failed to update non-framework location access proxy apps in the"
- + " GNSS HAL to: " + proxyAppsStr);
- }
+ return locationPermissionEnabledProxyApps;
}
private void handleNfwNotification(NfwNotification nfwNotification) {
@@ -360,7 +459,31 @@
isPermissionMismatched);
}
- private void postEvent(Runnable event) {
+ private void listenForRoamingNetworkUpdate() {
+ // Register for network capabilities changes to monitor roaming changes.
+ ConnectivityManager mConnMgr = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
+ networkRequestBuilder.addCapability(NetworkCapabilities.TRANSPORT_CELLULAR);
+ NetworkRequest networkRequest = networkRequestBuilder.build();
+ mConnMgr.registerNetworkCallback(networkRequest,
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(Network network,
+ NetworkCapabilities capabilities) {
+ boolean isRoaming = !capabilities.hasTransport(
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+ // No locking required for this test and set because the callback
+ // runs in mHandler thread.
+ if (mIsOnRoamingNetwork != isRoaming) {
+ mIsOnRoamingNetwork = isRoaming;
+ updateNfwLocationAccessProxyAppsInGnssHal();
+ }
+ }
+ }, mHandler);
+ }
+
+ private void runOnHandler(Runnable event) {
// Hold a wake lock until this message is delivered.
// Note that this assumes the message will not be removed from the queue before
// it is handled (otherwise the wake lock would be leaked).
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9100f6a..6eff815 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -449,8 +449,7 @@
private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
- private static final boolean PRECOMPILED_LAYOUT_ENABLED =
- SystemProperties.getBoolean("view.precompiled_layout_enabled", false);
+ private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -9119,7 +9118,7 @@
pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
- if (PRECOMPILED_LAYOUT_ENABLED) {
+ if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
mArtManagerService.compileLayouts(pkg);
}
@@ -16211,7 +16210,7 @@
if (performDexopt) {
// Compile the layout resources.
- if (PRECOMPILED_LAYOUT_ENABLED) {
+ if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 563fd7f..84c8b60 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -690,6 +690,10 @@
return result;
}
+ public boolean hasShareTargets() {
+ return !mShareTargets.isEmpty();
+ }
+
/**
* Return the filenames (excluding path names) of icon bitmap files from this package.
*/
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index fdbaba2..792b34c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2167,6 +2167,19 @@
}
}
+ @Override
+ public boolean hasShareTargets(String packageName, String packageToCheck,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ enforceSystem();
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ return getPackageShortcutsLocked(packageToCheck, userId).hasShareTargets();
+ }
+ }
+
@GuardedBy("mLock")
private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
@UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 8d64b81..2455113 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -16,6 +16,10 @@
package com.android.server.pm;
+import com.google.android.collect.Sets;
+
+import com.android.internal.util.Preconditions;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -38,10 +42,6 @@
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.collect.Sets;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -666,7 +666,6 @@
case android.provider.Settings.Secure.ALWAYS_ON_VPN_APP:
case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN:
- case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST:
// Whitelist system uid (ConnectivityService) and root uid to change always-on vpn
final int appId = UserHandle.getAppId(callingUid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0e40a00..13c4d88 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2569,6 +2569,7 @@
}
private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
};
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e1a911e..1d82970 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -825,16 +825,16 @@
// like the ANR / app crashed dialogs
return canAddInternalSystemWindow ? 11 : 10;
case TYPE_APPLICATION_OVERLAY:
- return 12;
+ return canAddInternalSystemWindow ? 13 : 12;
case TYPE_DREAM:
// used for Dreams (screensavers with TYPE_DREAM windows)
- return 13;
+ return 14;
case TYPE_INPUT_METHOD:
// on-screen keyboards and other such input method user interfaces go here.
- return 14;
+ return 15;
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
- return 15;
+ return 16;
case TYPE_STATUS_BAR:
return 17;
case TYPE_STATUS_BAR_PANEL:
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 055c941..7f2dedb 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.app.role.RoleManager;
+import android.content.ComponentName;
import android.content.Context;
import android.os.Debug;
import android.provider.Settings;
@@ -90,6 +91,17 @@
return CollectionUtils.singletonOrEmpty(result);
}
+ case RoleManager.ROLE_ASSISTANT: {
+ String legacyAssistant = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
+
+ if (legacyAssistant == null || legacyAssistant.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(
+ ComponentName.unflattenFromString(legacyAssistant).getPackageName());
+ }
+ }
default: {
Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName);
return Collections.emptyList();
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index c0517fd..1c7596b 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -198,6 +198,7 @@
// Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders
// for a given role before adding a migration statement for it here
migrateRoleIfNecessary(RoleManager.ROLE_SMS, userId);
+ migrateRoleIfNecessary(RoleManager.ROLE_ASSISTANT, userId);
// Some vital packages state has changed since last role grant
// Run grants again
diff --git a/services/core/java/com/android/server/rollback/LocalIntentReceiver.java b/services/core/java/com/android/server/rollback/LocalIntentReceiver.java
new file mode 100644
index 0000000..504a349
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/LocalIntentReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.IBinder;
+
+import java.util.function.Consumer;
+
+/** {@code IntentSender} implementation for RollbackManager internal use. */
+class LocalIntentReceiver {
+ final Consumer<Intent> mConsumer;
+
+ LocalIntentReceiver(Consumer<Intent> consumer) {
+ mConsumer = consumer;
+ }
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ mConsumer.accept(intent);
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 693d5d6..8b4c410 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -19,8 +19,6 @@
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -36,11 +34,9 @@
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.storage.StorageManager;
@@ -66,7 +62,6 @@
import java.util.Map;
import java.util.Random;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Implementation of service that manages APK level rollbacks.
@@ -116,6 +111,7 @@
private final Context mContext;
private final HandlerThread mHandlerThread;
private final Installer mInstaller;
+ private final RollbackPackageHealthObserver mPackageHealthObserver;
RollbackManagerServiceImpl(Context context) {
mContext = context;
@@ -128,6 +124,8 @@
mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
+ mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
+
// Kick off loading of the rollback data from strorage in a background
// thread.
// TODO: Consider loading the rollback data directly here instead, to
@@ -376,28 +374,32 @@
final LocalIntentReceiver receiver = new LocalIntentReceiver(
(Intent result) -> {
- // We've now completed the rollback, so we mark it as no longer in
- // progress.
- data.inProgress = false;
+ getHandler().post(() -> {
+ // We've now completed the rollback, so we mark it as no longer in
+ // progress.
+ data.inProgress = false;
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- sendFailure(statusReceiver, "Rollback downgrade install failed: "
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
- return;
- }
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ sendFailure(statusReceiver,
+ "Rollback downgrade install failed: "
+ + result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE));
+ return;
+ }
- addRecentlyExecutedRollback(rollback);
- sendSuccess(statusReceiver);
+ addRecentlyExecutedRollback(rollback);
+ sendSuccess(statusReceiver);
- Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
+ Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
- // TODO: This call emits the warning "Calling a method in the
- // system process without a qualified user". Fix that.
- // TODO: Limit this to receivers holding the
- // MANAGE_ROLLBACKS permission?
- mContext.sendBroadcast(broadcast);
+ // TODO: This call emits the warning "Calling a method in the
+ // system process without a qualified user". Fix that.
+ // TODO: Limit this to receivers holding the
+ // MANAGE_ROLLBACKS permission?
+ mContext.sendBroadcast(broadcast);
+ });
}
);
@@ -820,26 +822,6 @@
});
}
- private class LocalIntentReceiver {
- final Consumer<Intent> mConsumer;
-
- LocalIntentReceiver(Consumer<Intent> consumer) {
- mConsumer = consumer;
- }
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- getHandler().post(() -> mConsumer.accept(intent));
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
- }
-
/**
* Gets the version of the package currently installed.
* Returns null if the package is not currently installed.
@@ -906,7 +888,17 @@
ensureRollbackDataLoadedLocked();
mAvailableRollbacks.add(data);
}
-
+ // TODO(zezeozue): Provide API to explicitly start observing instead
+ // of doing this for all rollbacks. If we do this for all rollbacks,
+ // should document in PackageInstaller.SessionParams#setEnableRollback
+ // After enabling and commiting any rollback, observe packages and
+ // prepare to rollback if packages crashes too frequently.
+ List<String> packages = new ArrayList<>();
+ for (int i = 0; i < data.packages.size(); i++) {
+ packages.add(data.packages.get(i).getPackageName());
+ }
+ mPackageHealthObserver.startObservingHealth(packages,
+ ROLLBACK_LIFETIME_DURATION_MILLIS);
scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
} catch (IOException e) {
Log.e(TAG, "Unable to enable rollback", e);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
new file mode 100644
index 0000000..1f2f1cc
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.server.PackageWatchdog;
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+
+import java.util.List;
+
+/**
+ * {@code PackageHealthObserver} for {@code RollbackManagerService}.
+ *
+ * @hide
+ */
+public final class RollbackPackageHealthObserver implements PackageHealthObserver {
+ private static final String TAG = "RollbackPackageHealthObserver";
+ private static final String NAME = "rollback-observer";
+ private Context mContext;
+ private Handler mHandler;
+
+ RollbackPackageHealthObserver(Context context) {
+ mContext = context;
+ HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
+ handlerThread.start();
+ mHandler = handlerThread.getThreadHandler();
+ PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ }
+
+ @Override
+ public boolean onHealthCheckFailed(String packageName) {
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ RollbackInfo rollback = rollbackManager.getAvailableRollback(packageName);
+ if (rollback != null) {
+ // TODO(zezeozue): Only rollback if rollback version == failed package version
+ mHandler.post(() -> executeRollback(rollbackManager, rollback));
+ return true;
+ }
+ // Don't handle the notification, no rollbacks available
+ return false;
+ }
+
+ /**
+ * Start observing health of {@code packages} for {@code durationMs}.
+ * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
+ */
+ public void startObservingHealth(List<String> packages, long durationMs) {
+ PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
+ }
+
+ private void executeRollback(RollbackManager manager, RollbackInfo rollback) {
+ // TODO(zezeozue): Log initiated metrics
+ LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
+ mHandler.post(() -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ // TODO(zezeozue); Log success metrics
+ // Rolledback successfully, no action required by other observers
+ } else {
+ // TODO(zezeozue); Log failure metrics
+ // Rollback failed other observers should have a shot
+ }
+ });
+ });
+ manager.executeRollback(rollback, rollbackReceiver.getIntentSender());
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 891c3da..3a754c4 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2286,7 +2286,7 @@
* Check if the display to which this stack is attached has
* {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
*/
- private boolean canShowWithInsecureKeyguard() {
+ boolean canShowWithInsecureKeyguard() {
final ActivityDisplay activityDisplay = getDisplay();
if (activityDisplay == null) {
throw new IllegalStateException("Stack is not attached to any display, stackId="
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 865ffbd..3a077b8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -916,7 +916,8 @@
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
Intent intent) {
// don't abort for the most important UIDs
- if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.NFC_UID) {
return false;
}
// don't abort if the callerApp has any visible activity
@@ -924,14 +925,15 @@
return false;
}
// don't abort if the callingUid is in the foreground or is a persistent system process
- final boolean isCallingUidForeground = isUidForeground(callingUid);
+ final boolean isCallingUidForeground = mService.isUidForeground(callingUid);
final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
callingUid);
if (isCallingUidForeground || isCallingUidPersistentSystemProcess) {
return false;
}
// take realCallingUid into consideration
- final boolean isRealCallingUidForeground = isUidForeground(realCallingUid);
+ final boolean isRealCallingUidForeground = mService.isUidForeground(
+ realCallingUid);
final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
realCallingUid);
if (realCallingUid != callingUid) {
@@ -976,12 +978,6 @@
return true;
}
- /** Returns true if uid has a visible window or its process is in a top state. */
- private boolean isUidForeground(int uid) {
- return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
- || mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
- }
-
/** Returns true if uid is in a persistent state. */
private boolean isUidPersistentSystemProcess(int uid) {
return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 67b00b2..1a5e6a1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -489,4 +489,7 @@
*/
public abstract ActivityManager.TaskSnapshot getTaskSnapshot(int taskId,
boolean reducedResolution);
+
+ /** Returns true if uid has a visible window or its process is in a top state. */
+ public abstract boolean isUidForeground(int uid);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ea6f4cc..5fabde4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4399,6 +4399,27 @@
}
}
+ @Override
+ public void registerRemoteAnimationsForDisplay(int displayId,
+ RemoteAnimationDefinition definition) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "registerRemoteAnimations");
+ definition.setCallingPid(Binder.getCallingPid());
+ synchronized (mGlobalLock) {
+ final ActivityDisplay display = mRootActivityContainer.getActivityDisplay(displayId);
+ if (display == null) {
+ Slog.e(TAG, "Couldn't find display with id: " + displayId);
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ display.mDisplayContent.registerRemoteAnimations(definition);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
@Override
public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
@@ -5632,6 +5653,11 @@
return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT);
}
+ boolean isUidForeground(int uid) {
+ return (getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
+ || mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
+ }
+
/**
* @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
* the whitelist
@@ -7041,5 +7067,12 @@
return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution);
}
}
+
+ @Override
+ public boolean isUidForeground(int uid) {
+ synchronized (mGlobalLock) {
+ return ActivityTaskManagerService.this.isUidForeground(uid);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 49308b8..8f0a7c0 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -76,6 +76,7 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final WallpaperController mWallpaperControllerLocked;
+ private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
@@ -85,6 +86,10 @@
mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
}
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mRemoteAnimationDefinition = definition;
+ }
+
/**
* Handle application transition for given display.
*/
@@ -216,6 +221,21 @@
return mainWindow != null ? mainWindow.mAttrs : null;
}
+ RemoteAnimationAdapter getRemoteAnimationOverride(AppWindowToken animLpToken, int transit,
+ ArraySet<Integer> activityTypes) {
+ final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
+ if (definition != null) {
+ final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
+ if (adapter != null) {
+ return adapter;
+ }
+ }
+ if (mRemoteAnimationDefinition == null) {
+ return null;
+ }
+ return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+ }
+
/**
* Overrides the pending transition with the remote animation defined for the transition in the
* set of defined remote animations in the app window token.
@@ -229,11 +249,8 @@
if (animLpToken == null) {
return;
}
- final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
- if (definition == null) {
- return;
- }
- final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
+ final RemoteAnimationAdapter adapter =
+ getRemoteAnimationOverride(animLpToken, transit, activityTypes);
if (adapter != null) {
animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(
adapter);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 65b36a0..d915e10 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -105,6 +105,7 @@
import android.view.DisplayInfo;
import android.view.IApplicationToken;
import android.view.InputApplicationHandle;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -1621,6 +1622,17 @@
t.reparent(getSurfaceControl(), mTransitChangeLeash);
onAnimationLeashCreated(t, mTransitChangeLeash);
+ // Skip creating snapshot if this transition is controlled by a remote animator which
+ // doesn't need it.
+ ArraySet<Integer> activityTypes = new ArraySet<>();
+ activityTypes.add(getActivityType());
+ RemoteAnimationAdapter adapter =
+ mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
+ this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
+ if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
+ return;
+ }
+
if (mThumbnail == null && getTask() != null) {
final TaskSnapshotController snapshotCtrl = mWmService.mTaskSnapshotController;
final ArraySet<Task> tasks = new ArraySet<>();
@@ -1639,6 +1651,11 @@
return mTransitChangeLeash != null || isChangeTransition(mTransit);
}
+ @VisibleForTesting
+ AppWindowThumbnail getThumbnail() {
+ return mThumbnail;
+ }
+
@Override
void checkAppWindowsReadyToShow() {
if (allDrawn == mLastAllDrawn) {
@@ -2349,7 +2366,7 @@
AnimationAdapter thumbnailAdapter = null;
getAnimationBounds(mTmpPoint, mTmpRect);
- boolean isChanging = isChangeTransition(transit) && mThumbnail != null;
+ boolean isChanging = isChangeTransition(transit) && enter;
// Delaying animation start isn't compatible with remote animations at all.
if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
@@ -2368,11 +2385,13 @@
getDisplayContent().getDisplayInfo(), duration,
true /* isAppAnimation */, false /* isThumbnail */),
mWmService.mSurfaceAnimationRunner);
- thumbnailAdapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
- getDisplayContent().getDisplayInfo(), duration,
- true /* isAppAnimation */, true /* isThumbnail */),
- mWmService.mSurfaceAnimationRunner);
+ if (mThumbnail != null) {
+ thumbnailAdapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
+ getDisplayContent().getDisplayInfo(), duration,
+ true /* isAppAnimation */, true /* isThumbnail */),
+ mWmService.mSurfaceAnimationRunner);
+ }
mTransit = transit;
mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 650d0be..ce3fb51 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -41,6 +41,8 @@
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -522,6 +524,11 @@
mChangeListeners.remove(listener);
}
+ @VisibleForTesting
+ boolean containsListener(ConfigurationContainerListener listener) {
+ return mChangeListeners.contains(listener);
+ }
+
/**
* Must be called when new parent for the container was set.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8f976e7..7a9ff52 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -162,6 +162,7 @@
import android.view.InputDevice;
import android.view.InsetsState.InternalInsetType;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationDefinition;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -1056,11 +1057,6 @@
*/
void setInsetProvider(@InternalInsetType int type, WindowState win,
@Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
- if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL) {
- if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) {
- return;
- }
- }
mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
}
@@ -1091,6 +1087,10 @@
return mLastWindowForcedOrientation;
}
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mAppTransitionController.registerRemoteAnimations(definition);
+ }
+
/**
* Temporarily pauses rotation changes until resumed.
*
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 6d3c693..4006332 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -58,6 +58,7 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -2029,7 +2030,8 @@
of.set(displayFrames.mRestricted);
df.set(displayFrames.mRestricted);
pf.set(displayFrames.mRestricted);
- } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
+ } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT
+ || type == TYPE_APPLICATION_OVERLAY) {
// These dialogs are stable to interim decor changes.
cf.set(displayFrames.mStable);
of.set(displayFrames.mStable);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index e49e4c0..e798203 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,7 +16,13 @@
package com.android.server.wm;
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -59,6 +65,7 @@
*/
private boolean mServerVisible;
+ private final boolean mControllable;
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
DisplayContent displayContent) {
@@ -66,6 +73,15 @@
mSource = source;
mDisplayContent = displayContent;
mStateController = stateController;
+
+ final int type = source.getType();
+ if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) {
+ mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL;
+ } else if (type == TYPE_IME) {
+ mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME;
+ } else {
+ mControllable = false;
+ }
}
InsetsSource getSource() {
@@ -73,6 +89,13 @@
}
/**
+ * @return Whether the current flag configuration allows to control this source.
+ */
+ boolean isControllable() {
+ return mControllable;
+ }
+
+ /**
* Updates the window that currently backs this source.
*
* @param win The window that links to this source.
@@ -91,6 +114,9 @@
mSource.setFrame(new Rect());
} else {
mWin.setInsetProvider(this);
+ if (mControllingWin != null) {
+ updateControlForTarget(mControllingWin, true /* force */);
+ }
}
}
@@ -113,8 +139,12 @@
&& !mWin.mGivenInsetsPending);
}
- void updateControlForTarget(@Nullable WindowState target) {
- if (target == mControllingWin) {
+ void updateControlForTarget(@Nullable WindowState target, boolean force) {
+ if (mWin == null) {
+ mControllingWin = target;
+ return;
+ }
+ if (target == mControllingWin && !force) {
return;
}
if (target == null) {
@@ -161,7 +191,7 @@
}
boolean isClientVisible() {
- return ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
+ return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
}
private class ControlAdapter implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 32dbe96..bb0cbb1 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -163,18 +163,18 @@
}
private void onControlChanged(int type, @Nullable WindowState win) {
- if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
- return;
- }
final WindowState previous = mTypeWinControlMap.get(type);
if (win == previous) {
return;
}
- final InsetsSourceProvider controller = mControllers.get(type);
+ final InsetsSourceProvider controller = getSourceProvider(type);
if (controller == null) {
return;
}
- controller.updateControlForTarget(win);
+ if (!controller.isControllable()) {
+ return;
+ }
+ controller.updateControlForTarget(win, false /* force */);
if (previous != null) {
removeFromControlMaps(previous, type);
mPendingControlChanged.add(previous);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 177f244..b5be2ac5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -91,9 +91,7 @@
*/
boolean isKeyguardOrAodShowing(int displayId) {
return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
- && (displayId == DEFAULT_DISPLAY
- ? !isDisplayOccluded(DEFAULT_DISPLAY)
- : isShowingOnSecondaryDisplay(displayId));
+ && !isDisplayOccluded(displayId);
}
/**
@@ -101,10 +99,7 @@
* display, false otherwise
*/
boolean isKeyguardShowing(int displayId) {
- return mKeyguardShowing && !mKeyguardGoingAway
- && (displayId == DEFAULT_DISPLAY
- ? !isDisplayOccluded(DEFAULT_DISPLAY)
- : isShowingOnSecondaryDisplay(displayId));
+ return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
}
/**
@@ -152,14 +147,6 @@
updateKeyguardSleepToken();
}
- private boolean isShowingOnSecondaryDisplay(int displayId) {
- if (mSecondaryDisplayIdsShowing == null) return false;
- for (int showingId : mSecondaryDisplayIdsShowing) {
- if (displayId == showingId) return true;
- }
- return false;
- }
-
/**
* Called when Keyguard is going away.
*
@@ -473,8 +460,16 @@
if (stack.getTopDismissingKeyguardActivity() != null) {
mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
}
+ // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
+ if (mDisplayId != DEFAULT_DISPLAY) {
+ mOccluded |= stack.canShowWithInsecureKeyguard()
+ && controller.canDismissKeyguard();
+ }
}
- mOccluded |= controller.mWindowManager.isShowingDream();
+ // TODO(b/123372519): isShowingDream can only works on default display.
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ mOccluded |= controller.mWindowManager.isShowingDream();
+ }
// TODO(b/113840485): Handle app transition for individual display, and apply occluded
// state change to secondary displays.
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index f760b39..5f95691 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -322,8 +322,10 @@
mStartBounds = new Rect(startBounds);
mTmpRect.set(startBounds);
mTmpRect.offsetTo(0, 0);
- mThumbnailAdapter =
- new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect);
+ if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
+ mThumbnailAdapter =
+ new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect);
+ }
} else {
mStartBounds = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5eff7d8..e6581df 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2817,7 +2817,7 @@
public boolean isShowingDream() {
synchronized (mGlobalLock) {
- // TODO: fix this when dream can be shown on non-default display.
+ // TODO(b/123372519): Fix this when dream can be shown on non-default display.
return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c38a974..030ee09 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -52,6 +52,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.Watchdog;
@@ -330,6 +331,13 @@
return mRequiredAbi;
}
+ /** Returns ID of display overriding the configuration for this process, or
+ * INVALID_DISPLAY if no display is overriding. */
+ @VisibleForTesting
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
public void setDebugging(boolean debugging) {
mDebugging = debugging;
}
@@ -761,15 +769,15 @@
activityDisplay.registerConfigurationChangeListener(this);
}
- private void unregisterDisplayConfigurationListenerLocked() {
+ @VisibleForTesting
+ void unregisterDisplayConfigurationListenerLocked() {
if (mDisplayId == INVALID_DISPLAY) {
return;
}
final ActivityDisplay activityDisplay =
mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId);
if (activityDisplay != null) {
- mAtm.mRootActivityContainer.getActivityDisplay(
- mDisplayId).unregisterConfigurationChangeListener(this);
+ activityDisplay.unregisterConfigurationChangeListener(this);
}
mDisplayId = INVALID_DISPLAY;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ce5eb84..4f12010 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4367,6 +4367,12 @@
}
void startAnimation(Animation anim) {
+
+ // If we are an inset provider, all our animations are driven by the inset client.
+ if (mInsetProvider != null && mInsetProvider.isControllable()) {
+ return;
+ }
+
final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
@@ -4380,6 +4386,12 @@
}
private void startMoveAnimation(int left, int top) {
+
+ // If we are an inset provider, all our animations are driven by the inset client.
+ if (mInsetProvider != null && mInsetProvider.isControllable()) {
+ return;
+ }
+
if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ab53242..f79f9bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -70,17 +70,24 @@
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
+import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
+
+
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -233,11 +240,11 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
+import com.android.internal.util.StatLogger;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
@@ -1918,11 +1925,7 @@
}
AlarmManager getAlarmManager() {
- return mContext.getSystemService(AlarmManager.class);
- }
-
- ConnectivityManager getConnectivityManager() {
- return mContext.getSystemService(ConnectivityManager.class);
+ return (AlarmManager) mContext.getSystemService(AlarmManager.class);
}
IWindowManager getIWindowManager() {
@@ -6305,8 +6308,7 @@
* @throws UnsupportedOperationException if the package does not support being set as always-on.
*/
@Override
- public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown,
- List<String> lockdownWhitelist)
+ public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown)
throws SecurityException {
enforceProfileOrDeviceOwner(admin);
@@ -6314,23 +6316,11 @@
final long token = mInjector.binderClearCallingIdentity();
try {
if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
- Slog.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
- throw new ServiceSpecificException(
- DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, vpnPackage);
+ return false;
}
-
- if (vpnPackage != null && lockdown && lockdownWhitelist != null) {
- for (String packageName : lockdownWhitelist) {
- if (!isPackageInstalledForUser(packageName, userId)) {
- Slog.w(LOG_TAG, "Non-existent package in VPN whitelist: " + packageName);
- throw new ServiceSpecificException(
- DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, packageName);
- }
- }
- }
- // If some package is uninstalled after the check above, it will be ignored by CM.
- if (!mInjector.getConnectivityManager().setAlwaysOnVpnPackageForUser(
- userId, vpnPackage, lockdown, lockdownWhitelist)) {
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdown)) {
throw new UnsupportedOperationException();
}
DevicePolicyEventLogger
@@ -6347,40 +6337,16 @@
}
@Override
- public String getAlwaysOnVpnPackage(ComponentName admin) throws SecurityException {
- enforceProfileOrDeviceOwner(admin);
-
- final int userId = mInjector.userHandleGetCallingUserId();
- final long token = mInjector.binderClearCallingIdentity();
- try {
- return mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(userId);
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
- }
- }
-
- @Override
- public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
- enforceProfileOrDeviceOwner(admin);
-
- final int userId = mInjector.userHandleGetCallingUserId();
- final long token = mInjector.binderClearCallingIdentity();
- try {
- return mInjector.getConnectivityManager().isVpnLockdownEnabled(userId);
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
- }
- }
-
- @Override
- public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
+ public String getAlwaysOnVpnPackage(ComponentName admin)
throws SecurityException {
enforceProfileOrDeviceOwner(admin);
final int userId = mInjector.userHandleGetCallingUserId();
final long token = mInjector.binderClearCallingIdentity();
- try {
- return mInjector.getConnectivityManager().getVpnLockdownWhitelist(userId);
+ try{
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return connectivityManager.getAlwaysOnVpnPackageForUser(userId);
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
@@ -6405,7 +6371,7 @@
}
}
- private void forceWipeUser(int userId, String wipeReasonForUser) {
+ private void forceWipeUser(int userId, String wipeReasonForUser, boolean wipeSilently) {
boolean success = false;
try {
IActivityManager am = mInjector.getIActivityManager();
@@ -6416,7 +6382,7 @@
success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
if (!success) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
- } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) {
+ } else if (isManagedProfile(userId) && !wipeSilently) {
sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
@@ -6431,6 +6397,7 @@
if (!mHasFeature) {
return;
}
+ Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
@@ -6490,7 +6457,7 @@
internalReason,
/*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
} else {
- forceWipeUser(userId, wipeReasonForUser);
+ forceWipeUser(userId, wipeReasonForUser, (flags & WIPE_SILENTLY) != 0);
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
@@ -6852,7 +6819,9 @@
enforceDeviceOwner(who);
long token = mInjector.binderClearCallingIdentity();
try {
- mInjector.getConnectivityManager().setGlobalProxy(proxyInfo);
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ connectivityManager.setGlobalProxy(proxyInfo);
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 65eaf554..5861368 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1561,6 +1561,12 @@
traceEnd();
}
+ // Grants default permissions and defines roles
+ traceBeginAndSlog("StartRoleManagerService");
+ mSystemServiceManager.startService(new RoleManagerService(
+ mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
+ traceEnd();
+
// We need to always start this service, regardless of whether the
// FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
// of initializing various settings. It will internally modify its behavior
@@ -2011,12 +2017,6 @@
}
traceEnd();
- // Grants default permissions and defines roles
- traceBeginAndSlog("StartRoleManagerService");
- mSystemServiceManager.startService(new RoleManagerService(
- mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
- traceEnd();
-
// No dependency on Webview preparation in system server. But this should
// be completed before allowing 3rd party
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
diff --git a/services/net/java/android/net/shared/NetworkObserverRegistry.java b/services/net/java/android/net/shared/NetworkObserverRegistry.java
new file mode 100644
index 0000000..36945f5
--- /dev/null
+++ b/services/net/java/android/net/shared/NetworkObserverRegistry.java
@@ -0,0 +1,255 @@
+/*
+ * 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.shared;
+
+import static android.Manifest.permission.NETWORK_STACK;
+
+import android.content.Context;
+import android.net.INetd;
+import android.net.INetdUnsolicitedEventListener;
+import android.net.INetworkManagementEventObserver;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+/**
+ * A class for reporting network events to clients.
+ *
+ * Implements INetdUnsolicitedEventListener and registers with netd, and relays those events to
+ * all INetworkManagementEventObserver objects that have registered with it.
+ *
+ * TODO: Make the notifyXyz methods protected once subclasses (e.g., the NetworkManagementService
+ * subclass) no longer call them directly.
+ *
+ * TODO: change from RemoteCallbackList to direct in-process callbacks.
+ */
+public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub {
+
+ private final Context mContext;
+ private final Handler mDaemonHandler;
+ private static final String TAG = "NetworkObserverRegistry";
+
+ /**
+ * Constructs a new instance and registers it with netd.
+ * This method should only be called once since netd will reject multiple registrations from
+ * the same process.
+ */
+ public NetworkObserverRegistry(Context context, Handler handler, INetd netd)
+ throws RemoteException {
+ mContext = context;
+ mDaemonHandler = handler;
+ netd.registerUnsolicitedEventListener(this);
+ }
+
+ private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
+ new RemoteCallbackList<>();
+
+ /**
+ * Registers the specified observer and start sending callbacks to it.
+ * This method may be called on any thread.
+ */
+ public void registerObserver(INetworkManagementEventObserver observer) {
+ mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+ mObservers.register(observer);
+ }
+
+ /**
+ * Unregisters the specified observer and stop sending callbacks to it.
+ * This method may be called on any thread.
+ */
+ public void unregisterObserver(INetworkManagementEventObserver observer) {
+ mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+ mObservers.unregister(observer);
+ }
+
+ @FunctionalInterface
+ private interface NetworkManagementEventCallback {
+ void sendCallback(INetworkManagementEventObserver o) throws RemoteException;
+ }
+
+ private void invokeForAllObservers(NetworkManagementEventCallback eventCallback) {
+ final int length = mObservers.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ eventCallback.sendCallback(mObservers.getBroadcastItem(i));
+ } catch (RemoteException | RuntimeException e) {
+ }
+ }
+ } finally {
+ mObservers.finishBroadcast();
+ }
+ }
+
+ /**
+ * Notify our observers of a change in the data activity state of the interface
+ */
+ public void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
+ int uid, boolean fromRadio) {
+ invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
+ Integer.toString(type), isActive, tsNanos));
+ }
+
+ @Override
+ public void onInterfaceClassActivityChanged(boolean isActive,
+ int label, long timestamp, int uid) throws RemoteException {
+ final long timestampNanos;
+ if (timestamp <= 0) {
+ timestampNanos = SystemClock.elapsedRealtimeNanos();
+ } else {
+ timestampNanos = timestamp;
+ }
+ mDaemonHandler.post(() -> notifyInterfaceClassActivity(label, isActive,
+ timestampNanos, uid, false));
+ }
+
+ /**
+ * Notify our observers of a limit reached.
+ */
+ @Override
+ public void onQuotaLimitReached(String alertName, String ifName) throws RemoteException {
+ mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName));
+ }
+
+ /**
+ * Notify our observers of a limit reached.
+ */
+ public void notifyLimitReached(String limitName, String iface) {
+ invokeForAllObservers(o -> o.limitReached(limitName, iface));
+ }
+
+ @Override
+ public void onInterfaceDnsServerInfo(String ifName,
+ long lifetime, String[] servers) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers));
+ }
+
+ /**
+ * Notify our observers of DNS server information received.
+ */
+ public void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
+ invokeForAllObservers(o -> o.interfaceDnsServerInfo(iface, lifetime, addresses));
+ }
+
+ @Override
+ public void onInterfaceAddressUpdated(String addr,
+ String ifName, int flags, int scope) throws RemoteException {
+ final LinkAddress address = new LinkAddress(addr, flags, scope);
+ mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address));
+ }
+
+ /**
+ * Notify our observers of a new or updated interface address.
+ */
+ public void notifyAddressUpdated(String iface, LinkAddress address) {
+ invokeForAllObservers(o -> o.addressUpdated(iface, address));
+ }
+
+ @Override
+ public void onInterfaceAddressRemoved(String addr,
+ String ifName, int flags, int scope) throws RemoteException {
+ final LinkAddress address = new LinkAddress(addr, flags, scope);
+ mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address));
+ }
+
+ /**
+ * Notify our observers of a deleted interface address.
+ */
+ public void notifyAddressRemoved(String iface, LinkAddress address) {
+ invokeForAllObservers(o -> o.addressRemoved(iface, address));
+ }
+
+
+ @Override
+ public void onInterfaceAdded(String ifName) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceAdded(ifName));
+ }
+
+ /**
+ * Notify our observers of an interface addition.
+ */
+ public void notifyInterfaceAdded(String iface) {
+ invokeForAllObservers(o -> o.interfaceAdded(iface));
+ }
+
+ @Override
+ public void onInterfaceRemoved(String ifName) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName));
+ }
+
+ /**
+ * Notify our observers of an interface removal.
+ */
+ public void notifyInterfaceRemoved(String iface) {
+ invokeForAllObservers(o -> o.interfaceRemoved(iface));
+ }
+
+ @Override
+ public void onInterfaceChanged(String ifName, boolean up) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));
+ }
+
+ /**
+ * Notify our observers of an interface status change
+ */
+ public void notifyInterfaceStatusChanged(String iface, boolean up) {
+ invokeForAllObservers(o -> o.interfaceStatusChanged(iface, up));
+ }
+
+ @Override
+ public void onInterfaceLinkStateChanged(String ifName, boolean up) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));
+ }
+
+ /**
+ * Notify our observers of an interface link state change
+ * (typically, an Ethernet cable has been plugged-in or unplugged).
+ */
+ public void notifyInterfaceLinkStateChanged(String iface, boolean up) {
+ invokeForAllObservers(o -> o.interfaceLinkStateChanged(iface, up));
+ }
+
+ @Override
+ public void onRouteChanged(boolean updated,
+ String route, String gateway, String ifName) throws RemoteException {
+ final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
+ ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
+ ifName);
+ mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute));
+ }
+
+ /**
+ * Notify our observers of a route change.
+ */
+ public void notifyRouteChange(boolean updated, RouteInfo route) {
+ if (updated) {
+ invokeForAllObservers(o -> o.routeUpdated(route));
+ } else {
+ invokeForAllObservers(o -> o.routeRemoved(route));
+ }
+ }
+
+ @Override
+ public void onStrictCleartextDetected(int uid, String hex) throws RemoteException {
+ // Don't do anything here because this is not a method of INetworkManagementEventObserver.
+ // Only the NMS subclass will implement this.
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index d91ce39..de7d77d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -158,7 +158,7 @@
boolean detectShortcutTrigger) {
MagnificationGestureHandler h = new MagnificationGestureHandler(
mContext, mMagnificationController,
- detectTripleTap, detectShortcutTrigger);
+ detectTripleTap, detectShortcutTrigger, DISPLAY_0);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index ac4a5fe..a3f36b7 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -24,11 +24,15 @@
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
@@ -39,21 +43,21 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.test.FakeSettingsProvider;
-
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -99,10 +103,6 @@
@Mock
private Context mContextMock;
@Mock
- private File mSuppressFileMock;
- @Mock
- private File mSuppressFileParentMock;
- @Mock
private IBinder mAgentMock;
@Mock
private ParcelFileDescriptor mParcelFileDescriptorMock;
@@ -114,95 +114,53 @@
private IBackupManagerMonitor mBackupManagerMonitorMock;
@Mock
private PrintWriter mPrintWriterMock;
+ @Mock
+ private UserManager mUserManagerMock;
+ @Mock
+ private UserInfo mUserInfoMock;
private FileDescriptor mFileDescriptorStub = new FileDescriptor();
private TrampolineTestable mTrampoline;
- private MockContentResolver mContentResolver;
+ private File mTestDir;
+ private File mSuppressFile;
+ private File mActivatedFile;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mUserId = NON_USER_SYSTEM;
+ mUserId = UserHandle.USER_SYSTEM;
SparseArray<UserBackupManagerService> serviceUsers = new SparseArray<>();
- serviceUsers.append(UserHandle.SYSTEM.getIdentifier(), mUserBackupManagerService);
+ serviceUsers.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
serviceUsers.append(NON_USER_SYSTEM, mUserBackupManagerService);
when(mBackupManagerServiceMock.getServiceUsers()).thenReturn(serviceUsers);
+ when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
+ when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+
TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
TrampolineTestable.sBackupDisabled = false;
+ TrampolineTestable.sUserManagerMock = mUserManagerMock;
- when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
+ mTestDir = InstrumentationRegistry.getContext().getFilesDir();
+ mTestDir.mkdirs();
+
+ mSuppressFile = new File(mTestDir, "suppress");
+ TrampolineTestable.sSuppressFile = mSuppressFile;
+
+ mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM);
+ TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile);
mTrampoline = new TrampolineTestable(mContextMock);
-
- mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContextMock.getContentResolver()).thenReturn(mContentResolver);
}
- @Test
- public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService();
-
- mTrampoline.unlockUser(UserHandle.USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).startServiceForUser(UserHandle.USER_SYSTEM);
- }
-
- @Test
- public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService();
-
- mTrampoline.unlockUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock, never()).startServiceForUser(NON_USER_SYSTEM);
- }
-
- @Test
- public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
- mTrampoline.initializeService();
-
- mTrampoline.unlockUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).startServiceForUser(NON_USER_SYSTEM);
- }
-
- @Test
- public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService();
-
- mTrampoline.stopUser(UserHandle.USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
- }
-
- @Test
- public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
- mTrampoline.initializeService();
-
- mTrampoline.stopUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock, never()).stopServiceForUser(NON_USER_SYSTEM);
- }
-
- @Test
- public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
- Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
-
- mTrampoline.initializeService();
-
- mTrampoline.stopUser(NON_USER_SYSTEM);
-
- verify(mBackupManagerServiceMock).stopServiceForUser(NON_USER_SYSTEM);
+ @After
+ public void tearDown() throws Exception {
+ mSuppressFile.delete();
+ mActivatedFile.delete();
}
@Test
@@ -222,18 +180,6 @@
assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
}
- // Verify that BackupManagerService is not initialized if suppress file exists.
- @Test
- public void initializeService_suppressFileExists_nonInitialized() throws Exception {
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
- trampoline.createBackupSuppressFileForUser(UserHandle.USER_SYSTEM);
-
-
- trampoline.initializeService();
-
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
@Test
public void initializeService_doesNotStartServiceForUsers() {
mTrampoline.initializeService();
@@ -247,15 +193,55 @@
}
@Test
- public void isBackupServiceActive_forNonSysUser_whenSysUserIsDeactivated_returnsFalse() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
+ mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
+ throws Exception {
+ mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
}
@Test
- public void setBackupServiceActive_callerSystemUid_serviceCreated() {
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
+ throws Exception {
+ mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ // Don't activate non-system user.
+
+ assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void
+ isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
+ throws Exception {
+ mTrampoline.initializeService();
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -264,7 +250,8 @@
}
@Test
- public void setBackupServiceActive_callerRootUid_serviceCreated() {
+ public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.ROOT_UID;
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -273,7 +260,8 @@
}
@Test
- public void setBackupServiceActive_callerNonRootNonSystem_securityExceptionThrown() {
+ public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
+ mTrampoline.initializeService();
TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
try {
@@ -281,14 +269,77 @@
fail();
} catch (SecurityException expected) {
}
+ }
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ @Test
+ public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.ROOT_UID;
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ mTrampoline.initializeService();
+ TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
+ mTrampoline.initializeService();
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(
+ eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
+ mTrampoline.initializeService();
+
+ try {
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
}
@Test
public void setBackupServiceActive_backupDisabled_ignored() {
TrampolineTestable.sBackupDisabled = true;
TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
+ trampoline.initializeService();
trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
@@ -296,14 +347,8 @@
}
@Test
- public void setBackupServiceActive_nonUserSystem_ignored() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
public void setBackupServiceActive_alreadyActive_ignored() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
assertEquals(1, mTrampoline.getCreateServiceCallsCount());
@@ -315,6 +360,7 @@
@Test
public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -323,6 +369,7 @@
@Test
public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
+ mTrampoline.initializeService();
mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
@@ -349,6 +396,21 @@
}
@Test
+ public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
+ mTrampoline.initializeService();
+ int otherUser = NON_USER_SYSTEM + 1;
+ File activateFile = new File(mTestDir, "activate-" + otherUser);
+ TrampolineTestable.sActivatedFiles.append(otherUser, activateFile);
+ mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+ assertFalse(mTrampoline.isBackupServiceActive(otherUser));
+ activateFile.delete();
+ }
+
+ @Test
public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
mTrampoline.dataChanged(PACKAGE_NAME);
verifyNoMoreInteractions(mBackupManagerServiceMock);
@@ -1123,7 +1185,7 @@
@Test
public void requestBackup_forwardedToCallingUserId() throws Exception {
TrampolineTestable.sCallingUserId = mUserId;
- when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
+ when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
mTrampoline.initializeService();
@@ -1227,50 +1289,33 @@
static int sCallingUserId = -1;
static int sCallingUid = -1;
static BackupManagerService sBackupManagerServiceMock = null;
+ static File sSuppressFile = null;
+ static SparseArray<File> sActivatedFiles = new SparseArray<>();
+ static UserManager sUserManagerMock = null;
private int mCreateServiceCallsCount = 0;
- private SparseArray<FakeFile> mSuppressFiles = new SparseArray<>();
-
- private static class FakeFile extends File {
- private boolean mExists;
-
- FakeFile(String pathname) {
- super(pathname);
- }
-
- @Override
- public boolean exists() {
- return mExists;
- }
-
- @Override
- public boolean delete() {
- mExists = false;
- return true;
- }
-
- @Override
- public boolean createNewFile() throws IOException {
- mExists = true;
- return true;
- }
- }
TrampolineTestable(Context context) {
super(context);
}
@Override
+ protected UserManager getUserManager() {
+ return sUserManagerMock;
+ }
+
+ @Override
public boolean isBackupDisabled() {
return sBackupDisabled;
}
@Override
- public File getSuppressFileForUser(int userId) {
- if (mSuppressFiles.get(userId) == null) {
- FakeFile file = new FakeFile(Integer.toString(userId));
- mSuppressFiles.append(userId, file);
- }
- return mSuppressFiles.get(userId);
+ protected File getSuppressFileForSystemUser() {
+ return sSuppressFile;
+ }
+
+ @Override
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return sActivatedFiles.get(userId);
}
protected int binderGetCallingUserId() {
@@ -1289,11 +1334,6 @@
}
@Override
- protected void createBackupSuppressFileForUser(int userId) throws IOException {
- getSuppressFileForUser(userId).createNewFile();
- }
-
- @Override
protected void postToHandler(Runnable runnable) {
runnable.run();
}
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index ca39a7f..0b01657 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -918,15 +918,14 @@
}
setAccessibilityColorInversion(true);
- setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
+ setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- assertAccessibilityTransformActivated(true /* activated */ );
- assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
- } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ assertUserColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+ if (isColorModeValid(ColorDisplayManager.COLOR_MODE_SATURATED)) {
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_SATURATED);
+ } else if (isColorModeValid(ColorDisplayManager.COLOR_MODE_AUTOMATIC)) {
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC);
}
}
@@ -937,15 +936,14 @@
}
setAccessibilityColorCorrection(true);
- setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
+ setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- assertAccessibilityTransformActivated(true /* activated */ );
- assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
- } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ assertUserColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+ if (isColorModeValid(ColorDisplayManager.COLOR_MODE_SATURATED)) {
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_SATURATED);
+ } else if (isColorModeValid(ColorDisplayManager.COLOR_MODE_AUTOMATIC)) {
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC);
}
}
@@ -957,15 +955,14 @@
setAccessibilityColorCorrection(true);
setAccessibilityColorInversion(true);
- setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
+ setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- assertAccessibilityTransformActivated(true /* activated */ );
- assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
- } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+ assertUserColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+ if (isColorModeValid(ColorDisplayManager.COLOR_MODE_SATURATED)) {
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_SATURATED);
+ } else if (isColorModeValid(ColorDisplayManager.COLOR_MODE_AUTOMATIC)) {
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC);
}
}
@@ -977,12 +974,11 @@
setAccessibilityColorCorrection(false);
setAccessibilityColorInversion(false);
- setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
+ setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- assertAccessibilityTransformActivated(false /* activated */ );
- assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
- assertActiveColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
+ assertUserColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+ assertActiveColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
}
/**
@@ -1098,17 +1094,6 @@
}
/**
- * Convenience method for asserting that Accessibility color transform is detected.
- *
- * @param state {@code true} if any Accessibility transform should be activated
- */
- private void assertAccessibilityTransformActivated(boolean state) {
- assertWithMessage("Unexpected Accessibility color transform state")
- .that(mColorDisplayController.getAccessibilityTransformActivated())
- .isEqualTo(state);
- }
-
- /**
* Convenience method for asserting that the active color mode matches expectation.
*
* @param mode the expected active color mode.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 5656289..056568a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -598,6 +598,10 @@
Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_nfcUid_notAborted", false,
+ Process.NFC_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false, false);
runAndVerifyBackgroundActivityStartsSubtest(
"disallowed_callingUidHasVisibleWindow_notAborted", false,
UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
new file mode 100644
index 0000000..19ace3c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.os.IBinder;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for change transitions
+ *
+ * Build/Install/Run:
+ * atest WmTests:AppChangeTransitionTests
+ */
+@FlakyTest(detail = "Promote when shown to be stable.")
+@SmallTest
+public class AppChangeTransitionTests extends WindowTestsBase {
+
+ private TaskStack mStack;
+ private Task mTask;
+ private WindowTestUtils.TestAppWindowToken mToken;
+
+ @Before
+ public void setUp() throws Exception {
+ mStack = createTaskStackOnDisplay(mDisplayContent);
+ mTask = createTaskInStack(mStack, 0 /* userId */);
+ mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+ mToken.mSkipOnParentSet = false;
+
+ mTask.addChild(mToken, 0);
+ }
+
+ class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
+ @Override
+ public void onAnimationStart(RemoteAnimationTarget[] apps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ for (RemoteAnimationTarget target : apps) {
+ assertNotNull(target.startBounds);
+ }
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (Exception e) {
+ throw new RuntimeException("Something went wrong");
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ }
+
+ @Test
+ public void testModeChangeRemoteAnimatorNoSnapshot() {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ RemoteAnimationAdapter adapter =
+ new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false);
+ definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter);
+ mDisplayContent.registerRemoteAnimations(definition);
+
+ mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(1, mDisplayContent.mChangingApps.size());
+ assertNull(mToken.getThumbnail());
+
+ waitUntilHandlersIdle();
+ mToken.removeImmediately();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 3740786..a498a1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -94,9 +94,9 @@
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar, null);
- mProvider.updateControlForTarget(target);
+ mProvider.updateControlForTarget(target, false /* force */);
assertNotNull(mProvider.getControl());
- mProvider.updateControlForTarget(null);
+ mProvider.updateControlForTarget(null, false /* force */);
assertNull(mProvider.getControl());
}
@@ -106,7 +106,7 @@
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar, null);
- mProvider.updateControlForTarget(target);
+ mProvider.updateControlForTarget(target, false /* force */);
InsetsState state = new InsetsState();
state.getSource(TYPE_TOP_BAR).setVisible(false);
mProvider.onInsetsModified(target, state.getSource(TYPE_TOP_BAR));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 9478be9..b867799 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -77,7 +77,7 @@
MockitoAnnotations.initMocks(this);
when(mMockRunner.asBinder()).thenReturn(new Binder());
- mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+ mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
mAdapter.setCallingPid(123);
mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0);
mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
new file mode 100644
index 0000000..a7c84a1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.content.pm.ApplicationInfo;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+/**
+ * Tests for the {@link WindowProcessController} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:WindowProcessControllerTests
+ */
+@Presubmit
+public class WindowProcessControllerTests extends ActivityTestsBase {
+
+ @Test
+ public void testDisplayConfigurationListener() {
+ final WindowProcessController wpc = new WindowProcessController(
+ mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
+ //By default, the process should not listen to any display.
+ assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+
+ // Register to display 1 as a listener.
+ TestActivityDisplay testActivityDisplay1 = createTestActivityDisplayInContainer();
+ wpc.registerDisplayConfigurationListenerLocked(testActivityDisplay1);
+ assertTrue(testActivityDisplay1.containsListener(wpc));
+ assertEquals(testActivityDisplay1.mDisplayId, wpc.getDisplayId());
+
+ // Move to display 2.
+ TestActivityDisplay testActivityDisplay2 = createTestActivityDisplayInContainer();
+ wpc.registerDisplayConfigurationListenerLocked(testActivityDisplay2);
+ assertFalse(testActivityDisplay1.containsListener(wpc));
+ assertTrue(testActivityDisplay2.containsListener(wpc));
+ assertEquals(testActivityDisplay2.mDisplayId, wpc.getDisplayId());
+
+ // Null ActivityDisplay will not change anything.
+ wpc.registerDisplayConfigurationListenerLocked(null);
+ assertTrue(testActivityDisplay2.containsListener(wpc));
+ assertEquals(testActivityDisplay2.mDisplayId, wpc.getDisplayId());
+
+ // Unregister listener will remove the wpc from registered displays.
+ wpc.unregisterDisplayConfigurationListenerLocked();
+ assertFalse(testActivityDisplay1.containsListener(wpc));
+ assertFalse(testActivityDisplay2.containsListener(wpc));
+ assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+
+ // Unregistration still work even if the display was removed.
+ wpc.registerDisplayConfigurationListenerLocked(testActivityDisplay1);
+ assertEquals(testActivityDisplay1.mDisplayId, wpc.getDisplayId());
+ mRootActivityContainer.removeChild(testActivityDisplay1);
+ wpc.unregisterDisplayConfigurationListenerLocked();
+ assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+ }
+
+ private TestActivityDisplay createTestActivityDisplayInContainer() {
+ final TestActivityDisplay testActivityDisplay = createNewActivityDisplay();
+ mRootActivityContainer.addChild(testActivityDisplay, POSITION_TOP);
+ return testActivityDisplay;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 44e998b..2263cf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -152,6 +152,7 @@
public static class TestAppWindowToken extends AppWindowToken {
boolean mOnTop = false;
private Transaction mPendingTransactionOverride;
+ boolean mSkipOnParentSet = true;
private TestAppWindowToken(DisplayContent dc) {
super(dc.mWmService, new IApplicationToken.Stub() {
@@ -200,7 +201,9 @@
@Override
void onParentSet() {
- // Do nothing.
+ if (!mSkipOnParentSet) {
+ super.onParentSet();
+ }
}
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index bb01f04..718f2d3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -17,13 +17,18 @@
package com.android.server.voiceinteraction;
import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -78,6 +83,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* SystemService that publishes an IVoiceInteractionManagerService.
@@ -200,6 +206,7 @@
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
+ new RoleObserver(mContext.getMainExecutor());
}
// TODO: VI Make sure the caller is the current user or profile
@@ -1268,6 +1275,106 @@
getActiveServiceComponentName());
}
+ class RoleObserver implements OnRoleHoldersChangedListener {
+ private PackageManager mPm = mContext.getPackageManager();
+ private RoleManager mRm = mContext.getSystemService(RoleManager.class);
+
+ RoleObserver(@NonNull @CallbackExecutor Executor executor) {
+ mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
+ }
+
+ private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
+ ResolveInfo resolveInfo = mPm.resolveServiceAsUser(
+ new Intent(RecognitionService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA, user.getIdentifier());
+
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ Log.w(TAG, "Unable to resolve default voice recognition service.");
+ return "";
+ }
+
+ return new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name).flattenToShortString();
+ }
+
+ /**
+ * Convert the assistant-role holder into settings. The rest of the system uses the
+ * settings.
+ *
+ * @param roleName the name of the role whose holders are changed
+ * @param user the user for this role holder change
+ */
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
+ if (!roleName.equals(RoleManager.ROLE_ASSISTANT)) {
+ return;
+ }
+
+ List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
+
+ if (roleHolders.isEmpty()) {
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.ASSISTANT, "");
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, "");
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user));
+ } else {
+ // Assistant is singleton role
+ String pkg = roleHolders.get(0);
+
+ // Try to set role holder as VoiceInteractionService
+ List<ResolveInfo> services = mPm.queryIntentServicesAsUser(
+ new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg),
+ PackageManager.GET_META_DATA, user.getIdentifier());
+
+ for (ResolveInfo resolveInfo : services) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+
+ VoiceInteractionServiceInfo voiceInteractionServiceInfo =
+ new VoiceInteractionServiceInfo(mPm, serviceInfo);
+ if (!voiceInteractionServiceInfo.getSupportsAssist()) {
+ continue;
+ }
+
+ String serviceComponentName = serviceInfo.getComponentName()
+ .flattenToShortString();
+
+ String serviceRecognizerName = new ComponentName(pkg,
+ voiceInteractionServiceInfo.getRecognitionService())
+ .flattenToShortString();
+
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.ASSISTANT, serviceComponentName);
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
+
+ return;
+ }
+
+ // If no service could be found try to set assist activity
+ final List<ResolveInfo> activities = mPm.queryIntentActivitiesAsUser(
+ new Intent(Intent.ACTION_ASSIST).setPackage(pkg),
+ PackageManager.MATCH_DEFAULT_ONLY, user.getIdentifier());
+
+ for (ResolveInfo resolveInfo : activities) {
+ ActivityInfo activityInfo = resolveInfo.activityInfo;
+
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.ASSISTANT,
+ activityInfo.getComponentName().flattenToShortString());
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_INTERACTION_SERVICE, "");
+ Settings.Secure.putString(getContext().getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE,
+ getDefaultRecognizer(user));
+ }
+ }
+ }
+ }
+
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 2b99ce1..2d29875 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -50,10 +50,10 @@
}
private CallAttributes(Parcel in) {
- mPreciseCallState = (PreciseCallState) in.readValue(mPreciseCallState.getClass()
- .getClassLoader());
+ mPreciseCallState = (PreciseCallState)
+ in.readValue(PreciseCallState.class.getClassLoader());
mNetworkType = in.readInt();
- mCallQuality = (CallQuality) in.readValue(mCallQuality.getClass().getClassLoader());
+ mCallQuality = (CallQuality) in.readValue(CallQuality.class.getClassLoader());
}
// getters
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a33b44c..349880d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -608,11 +608,41 @@
public static final String KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL =
"carrier_promote_wfc_on_call_fail_bool";
- /** Flag specifying whether provisioning is required for VOLTE. */
+ /**
+ * Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
+ * Calling.
+ */
public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
= "carrier_volte_provisioning_required_bool";
/**
+ * Flag indicating whether or not the IMS MmTel UT capability requires carrier provisioning
+ * before it can be set as enabled.
+ *
+ * If true, the UT capability will be set to false for the newly loaded subscription
+ * and will require the carrier provisioning app to set the persistent provisioning result.
+ * If false, the platform will not wait for provisioning status updates for the UT capability
+ * and enable the UT over IMS capability for the subscription when the subscription is loaded.
+ *
+ * The default value for this key is {@code false}.
+ */
+ public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL =
+ "carrier_ut_provisioning_required_bool";
+
+ /**
+ * Flag indicating whether or not the carrier supports Supplementary Services over the UT
+ * interface for this subscription.
+ *
+ * If true, the device will use Supplementary Services over UT when provisioned (see
+ * {@link #KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL}). If false, this device will fallback to
+ * circuit switch for supplementary services and will disable this capability for IMS entirely.
+ *
+ * The default value for this key is {@code true}.
+ */
+ public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL =
+ "carrier_supports_ss_over_ut_bool";
+
+ /**
* Flag specifying if WFC provisioning depends on VoLTE provisioning.
*
* {@code false}: default value; honor actual WFC provisioning state.
@@ -2575,6 +2605,8 @@
sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 0fa1b41..bca088e 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -91,20 +91,6 @@
"android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
/**
- * Intent action to select a profile to enable before download a new eSIM profile.
- *
- * May be called during device provisioning when there are multiple slots having profiles on
- * them. This Intent launches a screen for all the current existing profiles and let users to
- * choose which one they want to enable. In this case, the slot contains the profile will be
- * activated.
- *
- * @hide
- */
- @SystemApi
- public static final String ACTION_PROFILE_SELECTION =
- "android.telephony.euicc.action.PROFILE_SELECTION";
-
- /**
* Intent action to provision an embedded subscription.
*
* <p>May be called during device provisioning to launch a screen to perform embedded SIM
@@ -325,8 +311,8 @@
@IntDef(prefix = {"EUICC_ACTIVATION_"}, value = {
EUICC_ACTIVATION_TYPE_DEFAULT,
EUICC_ACTIVATION_TYPE_BACKUP,
- EUICC_ACTIVATION_TYPE_TRANSFER
-
+ EUICC_ACTIVATION_TYPE_TRANSFER,
+ EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED,
})
public @interface EuiccActivationType{}
@@ -360,6 +346,14 @@
@SystemApi
public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3;
+ /**
+ * The activation flow of eSIM requiring user account will be started. This can only be used
+ * when there is user account signed in. Otherwise, the flow will be failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4;
/**
* Euicc OTA update status which can be got by {@link #getOtaStatus}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 9414abd..5b2e635 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -86,9 +86,7 @@
/**
* Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
- * @hide
*/
- @SystemApi
public static final int WIFI_MODE_WIFI_PREFERRED = 2;
/**
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index d37198a..086a765 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -21,13 +21,17 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import com.android.internal.telephony.ITelephony;
@@ -38,13 +42,68 @@
* to changes in these configurations.
*
* Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
- * applications and may vary.
+ * applications and may vary. For compatibility purposes, the first 100 integer values used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Some common constants have been defined in this
+ * class to make integrating with other system apps easier. USE WITH CARE!
+ *
+ * To avoid collisions, please use String based configurations when possible:
+ * {@link #setProvisioningStringValue(int, String)} and {@link #getProvisioningStringValue(int)}.
* @hide
*/
@SystemApi
public class ProvisioningManager {
/**
+ * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+ */
+ public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
+ "STRING_QUERY_RESULT_ERROR_GENERIC";
+
+ /**
+ * The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
+ * ImsService implementation was not ready for provisioning queries.
+ */
+ public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
+ "STRING_QUERY_RESULT_ERROR_NOT_READY";
+
+ /**
+ * The integer result of provisioning for the queried key is disabled.
+ */
+ public static final int PROVISIONING_VALUE_DISABLED = 0;
+
+ /**
+ * The integer result of provisioning for the queried key is enabled.
+ */
+ public static final int PROVISIONING_VALUE_ENABLED = 1;
+
+
+ /**
+ * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
+ * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
+ * the subscription for WiFi Calling.
+ *
+ * @see #getProvisioningIntValue(int)
+ * @see #setProvisioningIntValue(int, int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
+
+ /**
+ * Override the user-defined WiFi mode for this subscription, defined in
+ * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning
+ * this subscription for WiFi Calling.
+ *
+ * Valid values for this key are:
+ * {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
+ * {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
+ * {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
+ *
+ * @see #getProvisioningIntValue(int)
+ * @see #setProvisioningIntValue(int, int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
+
+ /**
* Callback for IMS provisioning changes.
*/
public static class Callback {
@@ -180,10 +239,15 @@
/**
* Query for the integer value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
* @param key An integer that represents the provisioning key, which is defined by the OEM.
- * @return an integer value for the provided key.
+ * @return an integer value for the provided key, or
+ * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
* @throws IllegalArgumentException if the key provided was invalid.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getProvisioningIntValue(int key) {
try {
@@ -195,10 +259,16 @@
/**
* Query for the String value associated with the provided key.
- * @param key An integer that represents the provisioning key, which is defined by the OEM.
- * @return a String value for the provided key, or {@code null} if the key doesn't exist.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key A String that represents the provisioning key, which is defined by the OEM.
+ * @return a String value for the provided key, {@code null} if the key doesn't exist, or one
+ * of the following error codes: {@link #STRING_QUERY_RESULT_ERROR_GENERIC},
+ * {@link #STRING_QUERY_RESULT_ERROR_NOT_READY}.
* @throws IllegalArgumentException if the key provided was invalid.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getProvisioningStringValue(int key) {
try {
@@ -210,10 +280,16 @@
/**
* Set the integer value associated with the provided key.
+ *
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * Use {@link #setProvisioningStringValue(int, String)} with proper namespacing (to be defined
+ * per OEM or carrier) when possible instead to avoid key collision if needed.
* @param key An integer that represents the provisioning key, which is defined by the OEM.
* @param value a integer value for the provided key.
* @return the result of setting the configuration value.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
try {
@@ -226,10 +302,14 @@
/**
* Set the String value associated with the provided key.
*
- * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * This operation is blocking and should not be performed on the UI thread.
+ *
+ * @param key A String that represents the provisioning key, which is defined by the OEM and
+ * should be appropriately namespaced to avoid collision.
* @param value a String value for the provided key.
* @return the result of setting the configuration value.
*/
+ @WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
String value) {
@@ -240,6 +320,55 @@
}
}
+ /**
+ * Set the provisioning status for the IMS MmTel capability using the specified subscription.
+ *
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setProvisioningStatusForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+ try {
+ getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
+ isProvisioned);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the provisioning status for the IMS MmTel capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link MmTelFeature.MmTelCapabilities.MmTelCapability} and
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
+ * always return {@code true}.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean getProvisioningStatusForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().getImsProvisioningStatusForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
private static SubscriptionManager getSubscriptionManager(Context context) {
SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
if (manager == null) {
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index 7c793a5..1ee8563 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -97,6 +97,13 @@
public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
return radioTech;
}
+
+ @Override
+ public String toString() {
+ return "CapabilityPair{"
+ + "mCapability=" + mCapability
+ + ", radioTech=" + radioTech + '}';
+ }
}
// Pair contains <radio tech, mCapability>
@@ -212,6 +219,13 @@
}
}
+ @Override
+ public String toString() {
+ return "CapabilityChangeRequest{"
+ + "mCapabilitiesToEnable=" + mCapabilitiesToEnable
+ + ", mCapabilitiesToDisable=" + mCapabilitiesToDisable + '}';
+ }
+
/**
* @hide
*/
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 71a2174..4fc6a19 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -277,12 +277,14 @@
* Wi-Fi calling roaming status.
* Value is in Integer format. ON (1), OFF(0).
*/
- public static final int VOICE_OVER_WIFI_ROAMING = 26;
+ public static final int VOICE_OVER_WIFI_ROAMING =
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE;
/**
* Wi-Fi calling modem - WfcModeFeatureValueConstants.
* Value is in Integer format.
*/
- public static final int VOICE_OVER_WIFI_MODE = 27;
+ public static final int VOICE_OVER_WIFI_MODE =
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE;
/**
* VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
* Value is in Integer format.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9cc173c..8237d39 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1765,6 +1765,24 @@
void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
/**
+ * Set the provisioning status for the IMS MmTel capability using the specified subscription.
+ */
+ void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
+ boolean isProvisioned);
+
+ /**
+ * Get the provisioning status for the IMS MmTel capability specified.
+ */
+ boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech);
+
+ /** Is the capability and tech flagged as provisioned in the cache */
+ boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
+
+ /** Set the provisioning for the capability and tech in the cache */
+ void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
+ boolean isProvisioned);
+
+ /**
* Return an integer containing the provisioning value for the specified provisioning key.
*/
int getImsProvisioningInt(int subId, int key);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 0edc002..c76d153 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,10 +284,6 @@
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
- // If the device identifier check is enabled then enforce the new access requirements for
- // both 1P and 3P apps.
- boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 1;
// Check if the application is a 3P app; if so then a separate setting is required to relax
// the check to begin flagging problems with 3P apps early.
boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -300,6 +296,11 @@
context.getContentResolver(),
Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
boolean isNonPrivApp = false;
+ // Similar to above support relaxing the check for privileged apps while still enforcing it
+ // for non-privileged and 3P apps.
+ boolean relaxPrivDeviceIdentifierCheck = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_PRIV_CHECK_RELAXED, 0) == 1;
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
@@ -315,37 +316,28 @@
Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
e);
}
- Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
- + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
- // The new Q restrictions for device identifier access will be enforced if any of the
- // following are true:
- // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
- // - The app requesting a device identifier is not a preloaded app (3P), and the
- // PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
- // - The app requesting a device identifier is a preloaded app but is not a privileged app,
- // and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
- if (enableDeviceIdentifierCheck
+ // The new Q restrictions for device identifier access will be enforced for all apps with
+ // settings to individually disable the new restrictions for privileged, preloaded
+ // non-privileged, and 3P apps.
+ if ((!is3PApp && !isNonPrivApp && !relaxPrivDeviceIdentifierCheck)
|| (is3PApp && !relax3PDeviceIdentifierCheck)
|| (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
- boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
- if (callingPackage != null) {
- // if the target SDK is pre-Q or the target Q behavior is disabled then check if
- // the calling package would have previously had access to device identifiers.
- if (callingPackageInfo != null && (
- callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q
- || targetQBehaviorDisabled)) {
- if (context.checkPermission(
- android.Manifest.permission.READ_PHONE_STATE,
- pid,
- uid) == PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- if (SubscriptionManager.isValidSubscriptionId(subId)
- && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- return false;
- }
+ Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+ + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+ // if the target SDK is pre-Q then check if the calling package would have previously
+ // had access to device identifiers.
+ if (callingPackageInfo != null && (
+ callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
+ if (context.checkPermission(
+ android.Manifest.permission.READ_PHONE_STATE,
+ pid,
+ uid) == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ if (SubscriptionManager.isValidSubscriptionId(subId)
+ && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return false;
}
}
throw new SecurityException(message + ": The user " + uid
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 34aa258..780bb24 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -36,6 +36,17 @@
include $(BUILD_PACKAGE)
ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
+# RollbackTestAppACrashingV2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE)
+
# RollbackTestAppBv1.apk
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
@@ -68,6 +79,7 @@
LOCAL_JAVA_RESOURCE_FILES := \
$(ROLLBACK_TEST_APP_AV1) \
$(ROLLBACK_TEST_APP_AV2) \
+ $(ROLLBACK_TEST_APP_A_CRASHING_V2) \
$(ROLLBACK_TEST_APP_BV1) \
$(ROLLBACK_TEST_APP_BV2)
LOCAL_SDK_VERSION := system_current
@@ -77,5 +89,6 @@
# Clean up local variables
ROLLBACK_TEST_APP_AV1 :=
ROLLBACK_TEST_APP_AV2 :=
+ROLLBACK_TEST_APP_A_CRASHING_V2 :=
ROLLBACK_TEST_APP_BV1 :=
ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/TestApp/ACrashingV2.xml b/tests/RollbackTest/TestApp/ACrashingV2.xml
new file mode 100644
index 0000000..5708d23
--- /dev/null
+++ b/tests/RollbackTest/TestApp/ACrashingV2.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.rollback.testapp.A"
+ android:versionCode="2"
+ android:versionName="2.0" >
+
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <application android:label="Rollback Test App A v2">
+ <meta-data android:name="version" android:value="2" />
+ <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.tests.rollback.testapp.CrashingMainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
new file mode 100644
index 0000000..02a439b
--- /dev/null
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A crashing test app for testing apk rollback support.
+ */
+public class CrashingMainActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ throw new RuntimeException("Intended force crash");
+ }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 9d67cea..13ac4f0 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -38,6 +38,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -57,6 +58,7 @@
private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
+ private static final String INSTRUMENTED_APP = "com.android.tests.rollback";
/**
* Test basic rollbacks.
@@ -663,4 +665,63 @@
assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
+
+ // TODO(zezeozue): Stop ignoring after fixing race between rolling back and testing version
+ /**
+ * Test bad update automatic rollback.
+ */
+ @Ignore("Flaky")
+ @Test
+ public void testBadUpdateRollback() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ // Prep installation of the test apps.
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackTestUtils.uninstall(TEST_APP_B);
+ RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ // Both test apps should now be available for rollback, and the
+ // targetPackage returned for rollback should be correct.
+ // TODO: See if there is a way to remove this race condition
+ // between when the app is installed and when the rollback
+ // is made available.
+ Thread.sleep(1000);
+ RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+ assertNotNull(rollbackA);
+ assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName());
+
+ RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+ assertNotNull(rollbackB);
+ assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName());
+
+ // Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes
+ for (int i = 0; i < 5; i++) {
+ RollbackTestUtils.launchPackage(TEST_APP_A);
+ Thread.sleep(1000);
+ }
+ Thread.sleep(1000);
+
+ // TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ // Instrumented app is still the package installer
+ Context context = InstrumentationRegistry.getContext();
+ String installer = context.getPackageManager().getInstallerPackageName(TEST_APP_A);
+ assertEquals(INSTRUMENTED_APP, installer);
+ // TEST_APP_B is untouched
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index fbc3d8f..edb1355 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -135,6 +135,17 @@
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}
+ /** Launches {@code packageName} with {@link Intent#ACTION_MAIN}. */
+ static void launchPackage(String packageName)
+ throws InterruptedException, IOException {
+ Context context = InstrumentationRegistry.getContext();
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setPackage(packageName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ context.startActivity(intent);
+ }
+
/**
* Installs the apks with the given resource names as an atomic set.
*
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 5b17224..0b74d87 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -246,17 +246,17 @@
assertFalse(vpn.getLockdown());
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
assertTrue(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
assertTrue(vpn.getAlwaysOn());
assertTrue(vpn.getLockdown());
// Remove always-on configuration.
- assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(null, false));
assertFalse(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
}
@@ -270,11 +270,11 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -283,7 +283,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1]);
// Switch to another app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -297,87 +297,6 @@
}
@Test
- public void testLockdownWhitelist() throws Exception {
- final Vpn vpn = createVpn(primaryUser.id);
- final UidRange user = UidRange.createForUser(primaryUser.id);
-
- // Set always-on with lockdown and whitelist app PKGS[2] from lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2])));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
- }));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
-
- // Change whitelisted app to PKGS[3].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3])));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
- }));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
- new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
- }));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]);
- assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
-
- // Change the VPN app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3])));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
- }));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
- }));
- assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
-
- // Remove the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
- new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
- }));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.stop),
- }));
- assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
- user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[0]);
-
- // Add the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1])));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
- }));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
- }));
- assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
-
- // Try whitelisting a package with a comma, should be rejected.
- assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d")));
-
- // Pass a non-existent packages in the whitelist, they (and only they) should be ignored.
- // Whitelisted package should change from PGKS[1] to PKGS[2].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true,
- Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
- verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
- new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
- }));
- verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[]{
- new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
- new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
- }));
- }
-
- @Test
public void testLockdownAddingAProfile() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
setMockedUsers(primaryUser);
@@ -391,7 +310,7 @@
final UidRange profile = UidRange.createForUser(tempProfile.id);
// Set lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -517,7 +436,7 @@
.cancelAsUser(anyString(), anyInt(), eq(userHandle));
// Start showing a notification for disconnected once always-on.
- vpn.setAlwaysOnPackage(PKGS[0], false, null);
+ vpn.setAlwaysOnPackage(PKGS[0], false);
order.verify(mNotificationManager)
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
@@ -531,7 +450,7 @@
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
// Notification should be cleared after unsetting always-on package.
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false);
order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
}
@@ -664,9 +583,7 @@
doAnswer(invocation -> {
final String appName = (String) invocation.getArguments()[0];
final int userId = (int) invocation.getArguments()[1];
- Integer appId = packages.get(appName);
- if (appId == null) throw new PackageManager.NameNotFoundException(appName);
- return UserHandle.getUid(userId, appId);
+ return UserHandle.getUid(userId, packages.get(appName));
}).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
} catch (Exception e) {
}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index f2ecef9..e57433a 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -202,9 +202,11 @@
final CountDownLatch latch = new CountDownLatch(1);
functor.accept(latch);
try {
- latch.await(5000, TimeUnit.MILLISECONDS);
+ if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
+ fail(timeoutMessage);
+ }
} catch (InterruptedException e) {
- fail(timeoutMessage);
+ fail("Thread was interrupted");
}
}
@@ -314,6 +316,7 @@
assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
assertNull(key);
assertNull(attr);
+ latch.countDown();
})));
}
@@ -383,6 +386,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(FAKE_KEYS[5], key);
+ latch.countDown();
})));
// MTU matches key 4 but v4 address matches key 5. The latter is stronger.
@@ -392,6 +396,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(FAKE_KEYS[5], key);
+ latch.countDown();
})));
// Closest to key 3 (indeed, identical)
@@ -402,6 +407,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(FAKE_KEYS[3], key);
+ latch.countDown();
})));
// Group hint alone must not be strong enough to override the rest
@@ -411,6 +417,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(FAKE_KEYS[3], key);
+ latch.countDown();
})));
// Still closest to key 3, though confidence is lower
@@ -421,6 +428,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(FAKE_KEYS[3], key);
+ latch.countDown();
})));
// But changing the MTU makes this closer to key 4
@@ -430,6 +438,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(FAKE_KEYS[4], key);
+ latch.countDown();
})));
// MTU alone not strong enough to make this group-close
@@ -441,6 +450,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertNull(key);
+ latch.countDown();
})));
}
@@ -450,6 +460,7 @@
assertTrue("Retrieve network sameness not successful : " + status.resultCode,
status.isSuccess());
assertEquals(sameness, answer.getNetworkSameness());
+ latch.countDown();
})));
}
@@ -488,6 +499,7 @@
+ status.resultCode, status.isSuccess());
assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
assertNull(answer);
+ latch.countDown();
})));
}
}
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
index 9b5df56..06ff05e 100644
--- a/tools/processors/view_inspector/Android.bp
+++ b/tools/processors/view_inspector/Android.bp
@@ -1,6 +1,8 @@
-java_library_host {
+java_plugin {
name: "view-inspector-annotation-processor",
+ processor_class: "android.processor.view.inspector.PlatformInspectableProcessor",
+
srcs: ["src/java/**/*.java"],
java_resource_dirs: ["src/resources"],
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index d2d711f..96493de 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -787,6 +787,18 @@
public boolean trusted;
/**
+ * This Wifi configuration is created from a {@link WifiNetworkSuggestion}
+ * @hide
+ */
+ public boolean fromWifiNetworkSuggestion;
+
+ /**
+ * This Wifi configuration is created from a {@link WifiNetworkSpecifier}
+ * @hide
+ */
+ public boolean fromWifiNetworkSpecifier;
+
+ /**
* Indicates if the creator of this configuration has expressed that it
* should be considered metered.
*
@@ -1668,6 +1680,8 @@
ephemeral = false;
osu = false;
trusted = true; // Networks are considered trusted by default.
+ fromWifiNetworkSuggestion = false;
+ fromWifiNetworkSpecifier = false;
meteredHint = false;
meteredOverride = METERED_OVERRIDE_NONE;
useExternalScores = false;
@@ -1779,10 +1793,13 @@
if (this.ephemeral) sbuf.append(" ephemeral");
if (this.osu) sbuf.append(" osu");
if (this.trusted) sbuf.append(" trusted");
+ if (this.fromWifiNetworkSuggestion) sbuf.append(" fromWifiNetworkSuggestion");
+ if (this.fromWifiNetworkSpecifier) sbuf.append(" fromWifiNetworkSpecifier");
if (this.meteredHint) sbuf.append(" meteredHint");
if (this.useExternalScores) sbuf.append(" useExternalScores");
if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
- || this.ephemeral || this.trusted || this.meteredHint || this.useExternalScores) {
+ || this.ephemeral || this.trusted || this.fromWifiNetworkSuggestion
+ || this.fromWifiNetworkSpecifier || this.meteredHint || this.useExternalScores) {
sbuf.append("\n");
}
if (this.meteredOverride != METERED_OVERRIDE_NONE) {
@@ -2270,6 +2287,8 @@
ephemeral = source.ephemeral;
osu = source.osu;
trusted = source.trusted;
+ fromWifiNetworkSuggestion = source.fromWifiNetworkSuggestion;
+ fromWifiNetworkSpecifier = source.fromWifiNetworkSpecifier;
meteredHint = source.meteredHint;
meteredOverride = source.meteredOverride;
useExternalScores = source.useExternalScores;
@@ -2347,6 +2366,8 @@
dest.writeInt(isLegacyPasspointConfig ? 1 : 0);
dest.writeInt(ephemeral ? 1 : 0);
dest.writeInt(trusted ? 1 : 0);
+ dest.writeInt(fromWifiNetworkSuggestion ? 1 : 0);
+ dest.writeInt(fromWifiNetworkSpecifier ? 1 : 0);
dest.writeInt(meteredHint ? 1 : 0);
dest.writeInt(meteredOverride);
dest.writeInt(useExternalScores ? 1 : 0);
@@ -2418,6 +2439,8 @@
config.isLegacyPasspointConfig = in.readInt() != 0;
config.ephemeral = in.readInt() != 0;
config.trusted = in.readInt() != 0;
+ config.fromWifiNetworkSuggestion = in.readInt() != 0;
+ config.fromWifiNetworkSpecifier = in.readInt() != 0;
config.meteredHint = in.readInt() != 0;
config.meteredOverride = in.readInt();
config.useExternalScores = in.readInt() != 0;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 40077e1..8086039 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -16,7 +16,7 @@
package android.net.wifi;
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.READ_WIFI_CREDENTIAL;
@@ -950,8 +950,7 @@
* which was created with {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()} flag
* set.
* <p>
- * Note: The broadcast is sent to the app only if it holds either one of
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * Note: The broadcast is sent to the app only if it holds
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission.
*
* @see #EXTRA_NETWORK_SUGGESTION
@@ -1183,7 +1182,7 @@
* containing configurations which they created.
*/
@Deprecated
- @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE})
+ @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
@@ -1199,7 +1198,7 @@
/** @hide */
@SystemApi
- @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
+ @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
@@ -1656,8 +1655,7 @@
* When the device decides to connect to one of the provided network suggestions, platform sends
* a directed broadcast {@link #ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION} to the app if
* the network was created with {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()}
- * flag set and the app holds either one of
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * flag set and the app holds
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission.
*<p>
* NOTE:
@@ -2290,7 +2288,6 @@
/**
* Return the results of the latest access point scan.
* @return the list of access points found in the most recent scan. An app must hold
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* in order to get valid results.
*/
@@ -2601,7 +2598,7 @@
* <p>
* Applications need to have the following permissions to start LocalOnlyHotspot: {@link
* android.Manifest.permission#CHANGE_WIFI_STATE} and {@link
- * android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}. Callers without
+ * android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. Callers without
* the permissions will trigger a {@link java.lang.SecurityException}.
* <p>
* @param callback LocalOnlyHotspotCallback for the application to receive updates about
@@ -2684,7 +2681,7 @@
* {@link LocalOnlyHotspotObserver#onStopped()} callbacks.
* <p>
* Applications should have the
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}
* permission. Callers without the permission will trigger a
* {@link java.lang.SecurityException}.
* <p>
diff --git a/wifi/java/android/net/wifi/aware/IdentityChangedListener.java b/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
index 81a06e8..a8b19b3 100644
--- a/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
+++ b/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
@@ -30,7 +30,7 @@
public class IdentityChangedListener {
/**
* @param mac The MAC address of the Aware discovery interface. The application must have the
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to get the actual MAC address,
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} to get the actual MAC address,
* otherwise all 0's will be provided.
*/
public void onIdentityChanged(byte[] mac) {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 1fa1fd5..8aef7a2 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -231,7 +231,7 @@
* <p>
* This version of the API attaches a listener to receive the MAC address of the Aware interface
* on startup and whenever it is updated (it is randomized at regular intervals for privacy).
- * The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
+ * The application must have the {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission to execute this attach request. Otherwise, use the
* {@link #attach(AttachCallback, Handler)} version. Note that aside from permission
* requirements this listener will wake up the host at regular intervals causing higher power
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 5f8841c..245b304 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -133,7 +133,7 @@
* An application must use the {@link DiscoverySession#close()} to
* terminate the publish discovery session once it isn't needed. This will free
* resources as well terminate any on-air transmissions.
- * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
+ * <p>The application must have the {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission to start a publish discovery session.
*
* @param publishConfig The {@link PublishConfig} specifying the
@@ -179,7 +179,7 @@
* An application must use the {@link DiscoverySession#close()} to
* terminate the subscribe discovery session once it isn't needed. This will free
* resources as well terminate any on-air transmissions.
- * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
+ * <p>The application must have the {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission to start a subscribe discovery session.
*
* @param subscribeConfig The {@link SubscribeConfig} specifying the
diff --git a/wifi/java/android/net/wifi/aware/package.html b/wifi/java/android/net/wifi/aware/package.html
index d5d962f6..c4f2e1f 100644
--- a/wifi/java/android/net/wifi/aware/package.html
+++ b/wifi/java/android/net/wifi/aware/package.html
@@ -15,7 +15,7 @@
<ul>
<li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
<li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
- <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
+ <li>{@link android.Manifest.permission#ACCESS_FINE_LOCATION}</li>
</ul>
<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi Aware
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 1bed914..052ab99 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1139,7 +1139,7 @@
* @param c is the channel created at {@link #initialize}
* @param listener for callbacks on success or failure. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void discoverPeers(Channel c, ActionListener listener) {
checkChannel(c);
c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
@@ -1183,7 +1183,7 @@
* @param config options as described in {@link WifiP2pConfig} class
* @param listener for callbacks on success or failure. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {
checkChannel(c);
checkP2pConfig(config);
@@ -1225,7 +1225,7 @@
* @param c is the channel created at {@link #initialize}
* @param listener for callbacks on success or failure. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void createGroup(Channel c, ActionListener listener) {
checkChannel(c);
c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
@@ -1256,7 +1256,7 @@
* @param config the configuration of a p2p group.
* @param listener for callbacks on success or failure. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void createGroup(@NonNull Channel c,
@Nullable WifiP2pConfig config,
@Nullable ActionListener listener) {
@@ -1344,7 +1344,7 @@
* @param servInfo is a local service information.
* @param listener for callbacks on success or failure. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void addLocalService(Channel c, WifiP2pServiceInfo servInfo, ActionListener listener) {
checkChannel(c);
checkServiceInfo(servInfo);
@@ -1454,7 +1454,7 @@
* @param c is the channel created at {@link #initialize}
* @param listener for callbacks on success or failure. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void discoverServices(Channel c, ActionListener listener) {
checkChannel(c);
c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, c.putListener(listener));
@@ -1530,7 +1530,7 @@
* @param c is the channel created at {@link #initialize}
* @param listener for callback when peer list is available. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void requestPeers(Channel c, PeerListListener listener) {
checkChannel(c);
c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
@@ -1553,7 +1553,7 @@
* @param c is the channel created at {@link #initialize}
* @param listener for callback when group info is available. Can be null.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void requestGroupInfo(Channel c, GroupInfoListener listener) {
checkChannel(c);
c.mAsyncChannel.sendMessage(REQUEST_GROUP_INFO, 0, c.putListener(listener));
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 7bff68a..449423f 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -60,6 +60,8 @@
config.setPasspointManagementObjectTree(cookie);
config.trusted = false;
config.updateIdentifier = "1234";
+ config.fromWifiNetworkSpecifier = true;
+ config.fromWifiNetworkSuggestion = true;
MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
Parcel parcelW = Parcel.obtain();
config.writeToParcel(parcelW, 0);
@@ -76,6 +78,8 @@
assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
assertFalse(reconfig.trusted);
+ assertTrue(config.fromWifiNetworkSpecifier);
+ assertTrue(config.fromWifiNetworkSuggestion);
Parcel parcelWW = Parcel.obtain();
reconfig.writeToParcel(parcelWW, 0);