diff options
214 files changed, 6907 insertions, 2784 deletions
diff --git a/api/current.txt b/api/current.txt index 993928680523..07a4f6d117d9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -71,6 +71,7 @@ package android { field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; + field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED"; field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS"; field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH"; field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER"; @@ -5900,6 +5901,7 @@ package android.app.admin { method public boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); + method public boolean isManagedProfile(android.content.ComponentName); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean isProfileOwnerApp(java.lang.String); @@ -9915,6 +9917,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80 field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400 field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10 + field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800 field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10 field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200 field public static final int PROTECTION_MASK_BASE = 15; // 0xf @@ -13503,6 +13506,7 @@ package android.hardware { public final class Sensor { method public int getFifoMaxEventCount(); method public int getFifoReservedEventCount(); + method public int getId(); method public int getMaxDelay(); method public float getMaximumRange(); method public int getMinDelay(); @@ -13512,9 +13516,10 @@ package android.hardware { method public float getResolution(); method public java.lang.String getStringType(); method public int getType(); - method public java.util.UUID getUuid(); method public java.lang.String getVendor(); method public int getVersion(); + method public boolean isAdditionalInfoSupported(); + method public boolean isDynamicSensor(); method public boolean isWakeUpSensor(); field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0 field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2 @@ -13633,8 +13638,9 @@ package android.hardware { method public static void getRotationMatrixFromVector(float[], float[]); method public java.util.List<android.hardware.Sensor> getSensorList(int); method public deprecated int getSensors(); - method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback); - method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler); + method public boolean isDynamicSensorDiscoverySupported(); + method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback); + method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler); method public deprecated boolean registerListener(android.hardware.SensorListener, int); method public deprecated boolean registerListener(android.hardware.SensorListener, int, int); method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int); @@ -13643,7 +13649,7 @@ package android.hardware { method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler); method public static boolean remapCoordinateSystem(float[], int, int, float[]); method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor); - method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback); + method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback); method public deprecated void unregisterListener(android.hardware.SensorListener); method public deprecated void unregisterListener(android.hardware.SensorListener, int); method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor); @@ -13708,8 +13714,8 @@ package android.hardware { field public static final float STANDARD_GRAVITY = 9.80665f; } - public static abstract class SensorManager.DynamicSensorConnectionCallback { - ctor public SensorManager.DynamicSensorConnectionCallback(); + public static abstract class SensorManager.DynamicSensorCallback { + ctor public SensorManager.DynamicSensorCallback(); method public void onDynamicSensorConnected(android.hardware.Sensor); method public void onDynamicSensorDisconnected(android.hardware.Sensor); } @@ -19806,7 +19812,7 @@ package android.media { public static abstract class AudioManager.AudioRecordingCallback { ctor public AudioManager.AudioRecordingCallback(); - method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]); + method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]); } public static abstract interface AudioManager.OnAudioFocusChangeListener { @@ -20096,7 +20102,7 @@ package android.media { field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5 field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7 field public static final int ORIENTATION_UNDEFINED = 0; // 0x0 - field public static final java.lang.String TAG_APERTURE = "FNumber"; + field public static final deprecated java.lang.String TAG_APERTURE = "FNumber"; field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue"; field public static final java.lang.String TAG_ARTIST = "Artist"; field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample"; @@ -20167,7 +20173,7 @@ package android.media { field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID"; field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth"; field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex"; - field public static final java.lang.String TAG_ISO = "ISOSpeedRatings"; + field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings"; field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings"; field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat"; field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength"; @@ -20575,12 +20581,13 @@ package android.media { field public static final int DolbyVisionLevelUhd30 = 64; // 0x40 field public static final int DolbyVisionLevelUhd48 = 128; // 0x80 field public static final int DolbyVisionLevelUhd60 = 256; // 0x100 - field public static final int DolbyVisionProfileDvavDen = 2; // 0x2 - field public static final int DolbyVisionProfileDvavDer = 1; // 0x1 - field public static final int DolbyVisionProfileDvheDen = 4; // 0x4 - field public static final int DolbyVisionProfileDvheDer = 3; // 0x3 - field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5 - field public static final int DolbyVisionProfileDvheStn = 6; // 0x6 + field public static final int DolbyVisionProfileDvavPen = 2; // 0x2 + field public static final int DolbyVisionProfileDvavPer = 1; // 0x1 + field public static final int DolbyVisionProfileDvheDen = 8; // 0x8 + field public static final int DolbyVisionProfileDvheDer = 4; // 0x4 + field public static final int DolbyVisionProfileDvheDth = 64; // 0x40 + field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10 + field public static final int DolbyVisionProfileDvheStn = 32; // 0x20 field public static final int H263Level10 = 1; // 0x1 field public static final int H263Level20 = 2; // 0x2 field public static final int H263Level30 = 4; // 0x4 @@ -36723,6 +36730,7 @@ package android.telephony { field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool"; field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string"; field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool"; + field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool"; field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; diff --git a/api/system-current.txt b/api/system-current.txt index a34f6c637923..fe562bb9014f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -103,6 +103,7 @@ package android { field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; + field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED"; field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"; field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS"; field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO"; @@ -6047,6 +6048,7 @@ package android.app.admin { method public boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); + method public boolean isManagedProfile(android.content.ComponentName); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean isProfileOwnerApp(java.lang.String); @@ -10314,6 +10316,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80 field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400 field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10 + field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800 field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10 field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200 field public static final int PROTECTION_MASK_BASE = 15; // 0xf @@ -13902,6 +13905,7 @@ package android.hardware { public final class Sensor { method public int getFifoMaxEventCount(); method public int getFifoReservedEventCount(); + method public int getId(); method public int getMaxDelay(); method public float getMaximumRange(); method public int getMinDelay(); @@ -13914,7 +13918,9 @@ package android.hardware { method public java.util.UUID getUuid(); method public java.lang.String getVendor(); method public int getVersion(); + method public boolean isAdditionalInfoSupported(); method public boolean isDataInjectionSupported(); + method public boolean isDynamicSensor(); method public boolean isWakeUpSensor(); field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0 field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2 @@ -14039,8 +14045,9 @@ package android.hardware { method public deprecated int getSensors(); method public boolean initDataInjection(boolean); method public boolean injectSensorData(android.hardware.Sensor, float[], int, long); - method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback); - method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler); + method public boolean isDynamicSensorDiscoverySupported(); + method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback); + method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler); method public deprecated boolean registerListener(android.hardware.SensorListener, int); method public deprecated boolean registerListener(android.hardware.SensorListener, int, int); method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int); @@ -14049,7 +14056,7 @@ package android.hardware { method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler); method public static boolean remapCoordinateSystem(float[], int, int, float[]); method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor); - method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback); + method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback); method public deprecated void unregisterListener(android.hardware.SensorListener); method public deprecated void unregisterListener(android.hardware.SensorListener, int); method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor); @@ -14114,8 +14121,8 @@ package android.hardware { field public static final float STANDARD_GRAVITY = 9.80665f; } - public static abstract class SensorManager.DynamicSensorConnectionCallback { - ctor public SensorManager.DynamicSensorConnectionCallback(); + public static abstract class SensorManager.DynamicSensorCallback { + ctor public SensorManager.DynamicSensorCallback(); method public void onDynamicSensorConnected(android.hardware.Sensor); method public void onDynamicSensorDisconnected(android.hardware.Sensor); } @@ -15264,6 +15271,7 @@ package android.hardware.location { ctor public ContextHubInfo(); method public int describeContents(); method public int getId(); + method public int getMaxPacketLengthBytes(); method public android.hardware.location.MemoryRegion[] getMemoryRegions(); method public java.lang.String getName(); method public float getPeakMips(); @@ -15291,10 +15299,6 @@ package android.hardware.location { method public int sendMessage(int, int, android.hardware.location.ContextHubMessage); method public int unloadNanoApp(int); method public int unregisterCallback(android.hardware.location.ContextHubManager.Callback); - field public static final int ANY_HUB = -1; // 0xffffffff - field public static final int MSG_DATA_SEND = 3; // 0x3 - field public static final int MSG_LOAD_NANO_APP = 1; // 0x1 - field public static final int MSG_UNLOAD_NANO_APP = 2; // 0x2 } public static abstract class ContextHubManager.Callback { @@ -15488,7 +15492,7 @@ package android.hardware.location { public class NanoAppInstanceInfo { ctor public NanoAppInstanceInfo(); method public int describeContents(); - method public int getAppId(); + method public long getAppId(); method public int getAppVersion(); method public int getContexthubId(); method public int getHandle(); @@ -15499,17 +15503,6 @@ package android.hardware.location { method public int getNeededWriteMemBytes(); method public int[] getOutputEvents(); method public java.lang.String getPublisher(); - method public void setAppId(int); - method public void setAppVersion(int); - method public void setContexthubId(int); - method public void setHandle(int); - method public void setName(java.lang.String); - method public void setNeededExecMemBytes(int); - method public void setNeededReadMemBytes(int); - method public void setNeededSensors(int[]); - method public void setNeededWriteMemBytes(int); - method public void setOutputEvents(int[]); - method public void setPublisher(java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR; } @@ -21295,7 +21288,7 @@ package android.media { public static abstract class AudioManager.AudioRecordingCallback { ctor public AudioManager.AudioRecordingCallback(); - method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]); + method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]); } public static abstract interface AudioManager.OnAudioFocusChangeListener { @@ -21588,7 +21581,7 @@ package android.media { field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5 field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7 field public static final int ORIENTATION_UNDEFINED = 0; // 0x0 - field public static final java.lang.String TAG_APERTURE = "FNumber"; + field public static final deprecated java.lang.String TAG_APERTURE = "FNumber"; field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue"; field public static final java.lang.String TAG_ARTIST = "Artist"; field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample"; @@ -21659,7 +21652,7 @@ package android.media { field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID"; field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth"; field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex"; - field public static final java.lang.String TAG_ISO = "ISOSpeedRatings"; + field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings"; field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings"; field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat"; field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength"; @@ -22067,12 +22060,13 @@ package android.media { field public static final int DolbyVisionLevelUhd30 = 64; // 0x40 field public static final int DolbyVisionLevelUhd48 = 128; // 0x80 field public static final int DolbyVisionLevelUhd60 = 256; // 0x100 - field public static final int DolbyVisionProfileDvavDen = 2; // 0x2 - field public static final int DolbyVisionProfileDvavDer = 1; // 0x1 - field public static final int DolbyVisionProfileDvheDen = 4; // 0x4 - field public static final int DolbyVisionProfileDvheDer = 3; // 0x3 - field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5 - field public static final int DolbyVisionProfileDvheStn = 6; // 0x6 + field public static final int DolbyVisionProfileDvavPen = 2; // 0x2 + field public static final int DolbyVisionProfileDvavPer = 1; // 0x1 + field public static final int DolbyVisionProfileDvheDen = 8; // 0x8 + field public static final int DolbyVisionProfileDvheDer = 4; // 0x4 + field public static final int DolbyVisionProfileDvheDth = 64; // 0x40 + field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10 + field public static final int DolbyVisionProfileDvheStn = 32; // 0x20 field public static final int H263Level10 = 1; // 0x1 field public static final int H263Level20 = 2; // 0x2 field public static final int H263Level30 = 4; // 0x4 @@ -24624,6 +24618,7 @@ package android.media.tv { method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo); method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon, int); + method public android.media.tv.TvInputInfo.Builder setLabel(java.lang.CharSequence); method public android.media.tv.TvInputInfo.Builder setLabel(int); method public android.media.tv.TvInputInfo.Builder setParentId(java.lang.String); method public android.media.tv.TvInputInfo.Builder setTunerCount(int); @@ -31327,8 +31322,8 @@ package android.os { field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1 field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6 - field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3 field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100 + field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3 field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1 field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0 field public static final int USER_ACTIVITY_EVENT_TOUCH = 2; // 0x2 @@ -32671,7 +32666,6 @@ package android.printservice.recommendation { method public final android.os.IBinder onBind(android.content.Intent); method public abstract void onConnected(); method public abstract void onDisconnected(); - method public final boolean onUnbind(android.content.Intent); method public final void updateRecommendations(java.util.List<android.printservice.recommendation.RecommendationInfo>); } @@ -39412,6 +39406,7 @@ package android.telephony { field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool"; field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string"; field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool"; + field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool"; field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; @@ -48855,6 +48850,24 @@ package android.webkit { method public abstract boolean shouldDelayChildPressedState(); } + public final class WebViewProviderInfo implements android.os.Parcelable { + ctor public WebViewProviderInfo(java.lang.String, java.lang.String, boolean, boolean, java.lang.String[]); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.webkit.WebViewProviderInfo> CREATOR; + field public final boolean availableByDefault; + field public final java.lang.String description; + field public final boolean isFallback; + field public final java.lang.String packageName; + field public final java.lang.String[] signatures; + } + + public final class WebViewUpdateService { + method public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages(); + method public static java.lang.String getCurrentWebViewPackageName(); + method public static android.webkit.WebViewProviderInfo[] getValidWebViewPackages(); + } + } package android.widget { diff --git a/api/test-current.txt b/api/test-current.txt index 56b758627810..09c55b927c60 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -71,6 +71,7 @@ package android { field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; + field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED"; field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS"; field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH"; field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER"; @@ -5904,6 +5905,7 @@ package android.app.admin { method public boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); + method public boolean isManagedProfile(android.content.ComponentName); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean isProfileOwnerApp(java.lang.String); @@ -9925,6 +9927,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80 field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400 field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10 + field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800 field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10 field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200 field public static final int PROTECTION_MASK_BASE = 15; // 0xf @@ -13513,6 +13516,7 @@ package android.hardware { public final class Sensor { method public int getFifoMaxEventCount(); method public int getFifoReservedEventCount(); + method public int getId(); method public int getMaxDelay(); method public float getMaximumRange(); method public int getMinDelay(); @@ -13522,9 +13526,10 @@ package android.hardware { method public float getResolution(); method public java.lang.String getStringType(); method public int getType(); - method public java.util.UUID getUuid(); method public java.lang.String getVendor(); method public int getVersion(); + method public boolean isAdditionalInfoSupported(); + method public boolean isDynamicSensor(); method public boolean isWakeUpSensor(); field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0 field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2 @@ -13643,8 +13648,9 @@ package android.hardware { method public static void getRotationMatrixFromVector(float[], float[]); method public java.util.List<android.hardware.Sensor> getSensorList(int); method public deprecated int getSensors(); - method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback); - method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler); + method public boolean isDynamicSensorDiscoverySupported(); + method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback); + method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler); method public deprecated boolean registerListener(android.hardware.SensorListener, int); method public deprecated boolean registerListener(android.hardware.SensorListener, int, int); method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int); @@ -13653,7 +13659,7 @@ package android.hardware { method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler); method public static boolean remapCoordinateSystem(float[], int, int, float[]); method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor); - method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback); + method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback); method public deprecated void unregisterListener(android.hardware.SensorListener); method public deprecated void unregisterListener(android.hardware.SensorListener, int); method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor); @@ -13718,8 +13724,8 @@ package android.hardware { field public static final float STANDARD_GRAVITY = 9.80665f; } - public static abstract class SensorManager.DynamicSensorConnectionCallback { - ctor public SensorManager.DynamicSensorConnectionCallback(); + public static abstract class SensorManager.DynamicSensorCallback { + ctor public SensorManager.DynamicSensorCallback(); method public void onDynamicSensorConnected(android.hardware.Sensor); method public void onDynamicSensorDisconnected(android.hardware.Sensor); } @@ -19871,7 +19877,7 @@ package android.media { public static abstract class AudioManager.AudioRecordingCallback { ctor public AudioManager.AudioRecordingCallback(); - method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]); + method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]); } public static abstract interface AudioManager.OnAudioFocusChangeListener { @@ -20161,7 +20167,7 @@ package android.media { field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5 field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7 field public static final int ORIENTATION_UNDEFINED = 0; // 0x0 - field public static final java.lang.String TAG_APERTURE = "FNumber"; + field public static final deprecated java.lang.String TAG_APERTURE = "FNumber"; field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue"; field public static final java.lang.String TAG_ARTIST = "Artist"; field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample"; @@ -20232,7 +20238,7 @@ package android.media { field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID"; field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth"; field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex"; - field public static final java.lang.String TAG_ISO = "ISOSpeedRatings"; + field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings"; field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings"; field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat"; field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength"; @@ -20640,12 +20646,13 @@ package android.media { field public static final int DolbyVisionLevelUhd30 = 64; // 0x40 field public static final int DolbyVisionLevelUhd48 = 128; // 0x80 field public static final int DolbyVisionLevelUhd60 = 256; // 0x100 - field public static final int DolbyVisionProfileDvavDen = 2; // 0x2 - field public static final int DolbyVisionProfileDvavDer = 1; // 0x1 - field public static final int DolbyVisionProfileDvheDen = 4; // 0x4 - field public static final int DolbyVisionProfileDvheDer = 3; // 0x3 - field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5 - field public static final int DolbyVisionProfileDvheStn = 6; // 0x6 + field public static final int DolbyVisionProfileDvavPen = 2; // 0x2 + field public static final int DolbyVisionProfileDvavPer = 1; // 0x1 + field public static final int DolbyVisionProfileDvheDen = 8; // 0x8 + field public static final int DolbyVisionProfileDvheDer = 4; // 0x4 + field public static final int DolbyVisionProfileDvheDth = 64; // 0x40 + field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10 + field public static final int DolbyVisionProfileDvheStn = 32; // 0x20 field public static final int H263Level10 = 1; // 0x1 field public static final int H263Level20 = 2; // 0x2 field public static final int H263Level30 = 4; // 0x4 @@ -36795,6 +36802,7 @@ package android.telephony { field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool"; field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string"; field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool"; + field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool"; field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index e520b406656b..7465ed92e469 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -2798,6 +2798,15 @@ public class AccountManager { if (account == null) { throw new IllegalArgumentException("account is null"); } + + // Always include the calling package name. This just makes life easier + // down stream. + final Bundle optionsIn = new Bundle(); + if (options != null) { + optionsIn.putAll(options); + } + optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); + return new AmsTask(activity, handler, callback) { @Override public void doWork() throws RemoteException { @@ -2806,7 +2815,7 @@ public class AccountManager { account, authTokenType, activity != null, - options); + optionsIn); } }.start(); } diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index d3ca7eebdfcd..4a1aff74ddf6 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -239,9 +239,6 @@ class ActivityTransitionState { public void onResume(Activity activity, boolean isTopOfTask) { // After orientation change, the onResume can come in before the top Activity has // left, so if the Activity is not top, wait a second for the top Activity to exit. - if (mCalledExitCoordinator == null) { - return; // This is the called activity - } if (isTopOfTask || mEnterTransitionCoordinator == null) { restoreExitedViews(); restoreReenteringViews(); diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index cb2130c4528e..e4fff9dc8a8c 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -905,10 +905,12 @@ public class AlarmManager { ListenerWrapper wrapper = null; synchronized (AlarmManager.class) { - final WeakReference<ListenerWrapper> wrapperRef; - wrapperRef = sWrappers.get(listener); - if (wrapperRef != null) { - wrapper = wrapperRef.get(); + if (sWrappers != null) { + final WeakReference<ListenerWrapper> wrapperRef; + wrapperRef = sWrappers.get(listener); + if (wrapperRef != null) { + wrapper = wrapperRef.get(); + } } } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 83dc506371cf..bd55a06c40f5 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -58,7 +58,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, * @param context the parent context */ public DatePickerDialog(@NonNull Context context) { - this(context, 0); + this(context, 0, null, Calendar.getInstance(), -1, -1, -1); } /** @@ -70,24 +70,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, * {@code context}'s default alert dialog theme */ public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) { - super(context, resolveDialogTheme(context, themeResId)); - - final Context themeContext = getContext(); - final LayoutInflater inflater = LayoutInflater.from(themeContext); - final View view = inflater.inflate(R.layout.date_picker_dialog, null); - setView(view); - - setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this); - setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this); - setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); - - final Calendar calendar = Calendar.getInstance(); - final int year = calendar.get(Calendar.YEAR); - final int monthOfYear = calendar.get(Calendar.MONTH); - final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); - mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); - mDatePicker.init(year, monthOfYear, dayOfMonth, this); - mDatePicker.setValidationCallback(mValidationCallback); + this(context, themeResId, null, Calendar.getInstance(), -1, -1, -1); } /** @@ -104,7 +87,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, */ public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) { - this(context, 0, listener, year, month, dayOfMonth); + this(context, 0, listener, null, year, month, dayOfMonth); } /** @@ -116,16 +99,40 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, * {@code context}'s default alert dialog theme * @param listener the listener to call when the user sets the date * @param year the initially selected year - * @param month the initially selected month (0-11 for compatibility with - * {@link Calendar#MONTH}) + * @param monthOfYear the initially selected month of the year (0-11 for + * compatibility with {@link Calendar#MONTH}) * @param dayOfMonth the initially selected day of month (1-31, depending * on month) */ public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId, - @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) { - this(context, themeResId); + @Nullable OnDateSetListener listener, int year, int monthOfYear, int dayOfMonth) { + this(context, themeResId, listener, null, year, monthOfYear, dayOfMonth); + } + + private DatePickerDialog(@NonNull Context context, @StyleRes int themeResId, + @Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year, + int monthOfYear, int dayOfMonth) { + super(context, resolveDialogTheme(context, themeResId)); + + final Context themeContext = getContext(); + final LayoutInflater inflater = LayoutInflater.from(themeContext); + final View view = inflater.inflate(R.layout.date_picker_dialog, null); + setView(view); + + setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this); + setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this); + setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); + + if (calendar != null) { + year = calendar.get(Calendar.YEAR); + monthOfYear = calendar.get(Calendar.MONTH); + dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); + } + + mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); + mDatePicker.init(year, monthOfYear, dayOfMonth, this); + mDatePicker.setValidationCallback(mValidationCallback); - mDatePicker.updateDate(year, month, dayOfMonth); mDateSetListener = listener; } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 0bb1097461fd..85a0403fe57e 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -16,6 +16,10 @@ package android.app; +import com.android.internal.R; +import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.policy.PhoneWindow; + import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.IdRes; @@ -43,7 +47,6 @@ import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; -import android.view.KeyboardShortcutGroup; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -57,12 +60,7 @@ import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; -import com.android.internal.R; -import com.android.internal.app.WindowDecorActionBar; -import com.android.internal.policy.PhoneWindow; - import java.lang.ref.WeakReference; -import java.util.List; /** * Base class for Dialogs. @@ -94,11 +92,14 @@ public class Dialog implements DialogInterface, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback { private static final String TAG = "Dialog"; private Activity mOwnerActivity; - + + private final WindowManager mWindowManager; + final Context mContext; - final WindowManager mWindowManager; - Window mWindow; + final Window mWindow; + View mDecor; + private ActionBar mActionBar; /** * This field should be made private, so it is hidden from the SDK. @@ -123,7 +124,7 @@ public class Dialog implements DialogInterface, Window.Callback, private static final int CANCEL = 0x44; private static final int SHOW = 0x45; - private Handler mListenersHandler; + private final Handler mListenersHandler; private SearchEvent mSearchEvent; @@ -131,11 +132,7 @@ public class Dialog implements DialogInterface, Window.Callback, private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY; - private final Runnable mDismissAction = new Runnable() { - public void run() { - dismissDialog(); - } - }; + private final Runnable mDismissAction = this::dismissDialog; /** * Creates a dialog window that uses the default dialog theme. @@ -198,14 +195,15 @@ public class Dialog implements DialogInterface, Window.Callback, * @hide */ @Deprecated - protected Dialog(@NonNull Context context, boolean cancelable, Message cancelCallback) { + protected Dialog(@NonNull Context context, boolean cancelable, + @Nullable Message cancelCallback) { this(context); mCancelable = cancelable; mCancelMessage = cancelCallback; } protected Dialog(@NonNull Context context, boolean cancelable, - OnCancelListener cancelListener) { + @Nullable OnCancelListener cancelListener) { this(context); mCancelable = cancelable; setOnCancelListener(cancelListener); @@ -216,8 +214,7 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return Context The Context used by the Dialog. */ - @NonNull - public final Context getContext() { + public final @NonNull Context getContext() { return mContext; } @@ -226,7 +223,7 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return The ActionBar attached to the dialog or null if no ActionBar is present. */ - public ActionBar getActionBar() { + public @Nullable ActionBar getActionBar() { return mActionBar; } @@ -236,7 +233,7 @@ public class Dialog implements DialogInterface, Window.Callback, * * @param activity The Activity that owns this dialog. */ - public final void setOwnerActivity(Activity activity) { + public final void setOwnerActivity(@NonNull Activity activity) { mOwnerActivity = activity; getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream()); @@ -250,7 +247,7 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return The Activity that owns this Dialog. */ - public final Activity getOwnerActivity() { + public final @Nullable Activity getOwnerActivity() { return mOwnerActivity; } @@ -316,13 +313,10 @@ public class Dialog implements DialogInterface, Window.Callback, l = nl; } - try { - mWindowManager.addView(mDecor, l); - mShowing = true; - - sendShowMessage(); - } finally { - } + mWindowManager.addView(mDecor, l); + mShowing = true; + + sendShowMessage(); } /** @@ -388,7 +382,7 @@ public class Dialog implements DialogInterface, Window.Callback, } } - // internal method to make sure mcreated is set properly without requiring + // internal method to make sure mCreated is set properly without requiring // users to call through to super in onCreate void dispatchOnCreate(Bundle savedInstanceState) { if (!mCreated) { @@ -400,7 +394,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * Similar to {@link Activity#onCreate}, you should initialize your dialog * in this method, including calling {@link #setContentView}. - * @param savedInstanceState If this dialog is being reinitalized after a + * @param savedInstanceState If this dialog is being reinitialized after a * the hosting activity was previously shut down, holds the result from * the most recent call to {@link #onSaveInstanceState}, or null if this * is the first time. @@ -433,7 +427,7 @@ public class Dialog implements DialogInterface, Window.Callback, * state. * @return A bundle with the state of the dialog. */ - public Bundle onSaveInstanceState() { + public @NonNull Bundle onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing); if (mCreated) { @@ -452,7 +446,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @param savedInstanceState The state of the dialog previously saved by * {@link #onSaveInstanceState()}. */ - public void onRestoreInstanceState(Bundle savedInstanceState) { + public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG); if (dialogHierarchyState == null) { // dialog has never been shown, or onCreated, nothing to restore. @@ -473,7 +467,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @return Window The current window, or null if the activity is not * visual. */ - public Window getWindow() { + public @Nullable Window getWindow() { return mWindow; } @@ -486,7 +480,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @see #getWindow * @see android.view.Window#getCurrentFocus */ - public View getCurrentFocus() { + public @Nullable View getCurrentFocus() { return mWindow != null ? mWindow.getCurrentFocus() : null; } @@ -498,8 +492,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @param id the identifier of the view to find * @return The view with the given id or null. */ - @Nullable - public View findViewById(@IdRes int id) { + public @Nullable View findViewById(@IdRes int id) { return mWindow.findViewById(id); } @@ -520,19 +513,19 @@ public class Dialog implements DialogInterface, Window.Callback, * * @param view The desired content to display. */ - public void setContentView(View view) { + public void setContentView(@NonNull View view) { mWindow.setContentView(view); } /** * Set the screen content to an explicit view. This view is placed * directly into the screen's view hierarchy. It can itself be a complex - * view hierarhcy. + * view hierarchy. * * @param view The desired content to display. * @param params Layout parameters for the view. */ - public void setContentView(View view, ViewGroup.LayoutParams params) { + public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) { mWindow.setContentView(view, params); } @@ -543,7 +536,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @param view The desired content to display. * @param params Layout parameters for the view. */ - public void addContentView(View view, ViewGroup.LayoutParams params) { + public void addContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) { mWindow.addContentView(view, params); } @@ -552,7 +545,7 @@ public class Dialog implements DialogInterface, Window.Callback, * * @param title The new text to display in the title. */ - public void setTitle(CharSequence title) { + public void setTitle(@Nullable CharSequence title) { mWindow.setTitle(title); mWindow.getAttributes().setTitle(title); } @@ -578,7 +571,8 @@ public class Dialog implements DialogInterface, Window.Callback, * @see #onKeyUp * @see android.view.KeyEvent */ - public boolean onKeyDown(int keyCode, KeyEvent event) { + @Override + public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { event.startTracking(); return true; @@ -592,7 +586,8 @@ public class Dialog implements DialogInterface, Window.Callback, * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle * the event). */ - public boolean onKeyLongPress(int keyCode, KeyEvent event) { + @Override + public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) { return false; } @@ -605,7 +600,8 @@ public class Dialog implements DialogInterface, Window.Callback, * @see #onKeyDown * @see KeyEvent */ - public boolean onKeyUp(int keyCode, KeyEvent event) { + @Override + public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() && !event.isCanceled()) { onBackPressed(); @@ -619,7 +615,8 @@ public class Dialog implements DialogInterface, Window.Callback, * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle * the event). */ - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, @NonNull KeyEvent event) { return false; } @@ -644,7 +641,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @param event Description of the key event. * @return True if the key shortcut was handled. */ - public boolean onKeyShortcut(int keyCode, KeyEvent event) { + public boolean onKeyShortcut(int keyCode, @NonNull KeyEvent event) { return false; } @@ -658,7 +655,7 @@ public class Dialog implements DialogInterface, Window.Callback, * The default implementation will cancel the dialog when a touch * happens outside of the window bounds. */ - public boolean onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(@NonNull MotionEvent event) { if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) { cancel(); return true; @@ -681,7 +678,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @return Return true if you have consumed the event, false if you haven't. * The default implementation always returns false. */ - public boolean onTrackballEvent(MotionEvent event) { + public boolean onTrackballEvent(@NonNull MotionEvent event) { return false; } @@ -710,25 +707,30 @@ public class Dialog implements DialogInterface, Window.Callback, * @return Return true if you have consumed the event, false if you haven't. * The default implementation always returns false. */ - public boolean onGenericMotionEvent(MotionEvent event) { + public boolean onGenericMotionEvent(@NonNull MotionEvent event) { return false; } + @Override public void onWindowAttributesChanged(WindowManager.LayoutParams params) { if (mDecor != null) { mWindowManager.updateViewLayout(mDecor, params); } } + @Override public void onContentChanged() { } + @Override public void onWindowFocusChanged(boolean hasFocus) { } + @Override public void onAttachedToWindow() { } + @Override public void onDetachedFromWindow() { } @@ -747,7 +749,8 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return boolean Return true if this event was consumed. */ - public boolean dispatchKeyEvent(KeyEvent event) { + @Override + public boolean dispatchKeyEvent(@NonNull KeyEvent event) { if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) { return true; } @@ -767,7 +770,8 @@ public class Dialog implements DialogInterface, Window.Callback, * @param event The key shortcut event. * @return True if this event was consumed. */ - public boolean dispatchKeyShortcutEvent(KeyEvent event) { + @Override + public boolean dispatchKeyShortcutEvent(@NonNull KeyEvent event) { if (mWindow.superDispatchKeyShortcutEvent(event)) { return true; } @@ -784,7 +788,8 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return boolean Return true if this event was consumed. */ - public boolean dispatchTouchEvent(MotionEvent ev) { + @Override + public boolean dispatchTouchEvent(@NonNull MotionEvent ev) { if (mWindow.superDispatchTouchEvent(ev)) { return true; } @@ -801,7 +806,8 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return boolean Return true if this event was consumed. */ - public boolean dispatchTrackballEvent(MotionEvent ev) { + @Override + public boolean dispatchTrackballEvent(@NonNull MotionEvent ev) { if (mWindow.superDispatchTrackballEvent(ev)) { return true; } @@ -818,14 +824,16 @@ public class Dialog implements DialogInterface, Window.Callback, * * @return boolean Return true if this event was consumed. */ - public boolean dispatchGenericMotionEvent(MotionEvent ev) { + @Override + public boolean dispatchGenericMotionEvent(@NonNull MotionEvent ev) { if (mWindow.superDispatchGenericMotionEvent(ev)) { return true; } return onGenericMotionEvent(ev); } - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + @Override + public boolean dispatchPopulateAccessibilityEvent(@NonNull AccessibilityEvent event) { event.setClassName(getClass().getName()); event.setPackageName(mContext.getPackageName()); @@ -840,6 +848,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onCreatePanelView(int) */ + @Override public View onCreatePanelView(int featureId) { return null; } @@ -847,7 +856,8 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onCreatePanelMenu(int, Menu) */ - public boolean onCreatePanelMenu(int featureId, Menu menu) { + @Override + public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) { if (featureId == Window.FEATURE_OPTIONS_PANEL) { return onCreateOptionsMenu(menu); } @@ -858,10 +868,10 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onPreparePanel(int, View, Menu) */ + @Override public boolean onPreparePanel(int featureId, View view, Menu menu) { if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) { - boolean goforit = onPrepareOptionsMenu(menu); - return goforit && menu.hasVisibleItems(); + return onPrepareOptionsMenu(menu) && menu.hasVisibleItems(); } return true; } @@ -869,6 +879,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onMenuOpened(int, Menu) */ + @Override public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR) { mActionBar.dispatchMenuVisibilityChanged(true); @@ -879,6 +890,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onMenuItemSelected(int, MenuItem) */ + @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { return false; } @@ -886,6 +898,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onPanelClosed(int, Menu) */ + @Override public void onPanelClosed(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR) { mActionBar.dispatchMenuVisibilityChanged(false); @@ -900,7 +913,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @see Activity#onCreateOptionsMenu(Menu) * @see #getOwnerActivity() */ - public boolean onCreateOptionsMenu(Menu menu) { + public boolean onCreateOptionsMenu(@NonNull Menu menu) { return true; } @@ -912,21 +925,21 @@ public class Dialog implements DialogInterface, Window.Callback, * @see Activity#onPrepareOptionsMenu(Menu) * @see #getOwnerActivity() */ - public boolean onPrepareOptionsMenu(Menu menu) { + public boolean onPrepareOptionsMenu(@NonNull Menu menu) { return true; } /** * @see Activity#onOptionsItemSelected(MenuItem) */ - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(@NonNull MenuItem item) { return false; } /** * @see Activity#onOptionsMenuClosed(Menu) */ - public void onOptionsMenuClosed(Menu menu) { + public void onOptionsMenuClosed(@NonNull Menu menu) { } /** @@ -959,47 +972,49 @@ public class Dialog implements DialogInterface, Window.Callback, /** * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) */ + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { } /** * @see Activity#registerForContextMenu(View) */ - public void registerForContextMenu(View view) { + public void registerForContextMenu(@NonNull View view) { view.setOnCreateContextMenuListener(this); } /** * @see Activity#unregisterForContextMenu(View) */ - public void unregisterForContextMenu(View view) { + public void unregisterForContextMenu(@NonNull View view) { view.setOnCreateContextMenuListener(null); } /** * @see Activity#openContextMenu(View) */ - public void openContextMenu(View view) { + public void openContextMenu(@NonNull View view) { view.showContextMenu(); } /** * @see Activity#onContextItemSelected(MenuItem) */ - public boolean onContextItemSelected(MenuItem item) { + public boolean onContextItemSelected(@NonNull MenuItem item) { return false; } /** * @see Activity#onContextMenuClosed(Menu) */ - public void onContextMenuClosed(Menu menu) { + public void onContextMenuClosed(@NonNull Menu menu) { } /** * This hook is called when the user signals the desire to start a search. */ - public boolean onSearchRequested(SearchEvent searchEvent) { + @Override + public boolean onSearchRequested(@NonNull SearchEvent searchEvent) { mSearchEvent = searchEvent; return onSearchRequested(); } @@ -1007,6 +1022,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * This hook is called when the user signals the desire to start a search. */ + @Override public boolean onSearchRequested() { final SearchManager searchManager = (SearchManager) mContext .getSystemService(Context.SEARCH_SERVICE); @@ -1029,13 +1045,10 @@ public class Dialog implements DialogInterface, Window.Callback, * @return SearchEvent The SearchEvent that triggered the {@link * #onSearchRequested} callback. */ - public final SearchEvent getSearchEvent() { + public final @Nullable SearchEvent getSearchEvent() { return mSearchEvent; } - /** - * {@inheritDoc} - */ @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { if (mActionBar != null && mActionModeTypeStarting == ActionMode.TYPE_PRIMARY) { @@ -1044,9 +1057,6 @@ public class Dialog implements DialogInterface, Window.Callback, return null; } - /** - * {@inheritDoc} - */ @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) { try { @@ -1063,6 +1073,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Note that if you override this method you should always call through * to the superclass implementation by calling super.onActionModeStarted(mode). */ + @Override @CallSuper public void onActionModeStarted(ActionMode mode) { mActionMode = mode; @@ -1074,6 +1085,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Note that if you override this method you should always call through * to the superclass implementation by calling super.onActionModeFinished(mode). */ + @Override @CallSuper public void onActionModeFinished(ActionMode mode) { if (mode == mActionMode) { @@ -1139,7 +1151,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Convenience for calling * {@link android.view.Window#setFeatureDrawableUri}. */ - public final void setFeatureDrawableUri(int featureId, Uri uri) { + public final void setFeatureDrawableUri(int featureId, @Nullable Uri uri) { getWindow().setFeatureDrawableUri(featureId, uri); } @@ -1147,7 +1159,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Convenience for calling * {@link android.view.Window#setFeatureDrawable(int, Drawable)}. */ - public final void setFeatureDrawable(int featureId, Drawable drawable) { + public final void setFeatureDrawable(int featureId, @Nullable Drawable drawable) { getWindow().setFeatureDrawable(featureId, drawable); } @@ -1159,7 +1171,7 @@ public class Dialog implements DialogInterface, Window.Callback, getWindow().setFeatureDrawableAlpha(featureId, alpha); } - public LayoutInflater getLayoutInflater() { + public @NonNull LayoutInflater getLayoutInflater() { return getWindow().getLayoutInflater(); } @@ -1191,6 +1203,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Cancel the dialog. This is essentially the same as calling {@link #dismiss()}, but it will * also call your {@link DialogInterface.OnCancelListener} (if registered). */ + @Override public void cancel() { if (!mCanceled && mCancelMessage != null) { mCanceled = true; @@ -1211,7 +1224,7 @@ public class Dialog implements DialogInterface, Window.Callback, * * @param listener The {@link DialogInterface.OnCancelListener} to use. */ - public void setOnCancelListener(final OnCancelListener listener) { + public void setOnCancelListener(@Nullable OnCancelListener listener) { if (mCancelAndDismissTaken != null) { throw new IllegalStateException( "OnCancelListener is already taken by " @@ -1229,7 +1242,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @param msg The msg to send when the dialog is canceled. * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener) */ - public void setCancelMessage(final Message msg) { + public void setCancelMessage(@Nullable Message msg) { mCancelMessage = msg; } @@ -1237,7 +1250,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Set a listener to be invoked when the dialog is dismissed. * @param listener The {@link DialogInterface.OnDismissListener} to use. */ - public void setOnDismissListener(final OnDismissListener listener) { + public void setOnDismissListener(@Nullable OnDismissListener listener) { if (mCancelAndDismissTaken != null) { throw new IllegalStateException( "OnDismissListener is already taken by " @@ -1254,7 +1267,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Sets a listener to be invoked when the dialog is shown. * @param listener The {@link DialogInterface.OnShowListener} to use. */ - public void setOnShowListener(OnShowListener listener) { + public void setOnShowListener(@Nullable OnShowListener listener) { if (listener != null) { mShowMessage = mListenersHandler.obtainMessage(SHOW, listener); } else { @@ -1266,13 +1279,13 @@ public class Dialog implements DialogInterface, Window.Callback, * Set a message to be sent when the dialog is dismissed. * @param msg The msg to send when the dialog is dismissed. */ - public void setDismissMessage(final Message msg) { + public void setDismissMessage(@Nullable Message msg) { mDismissMessage = msg; } /** @hide */ - public boolean takeCancelAndDismissListeners(String msg, final OnCancelListener cancel, - final OnDismissListener dismiss) { + public boolean takeCancelAndDismissListeners(@Nullable String msg, + @Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss) { if (mCancelAndDismissTaken != null) { mCancelAndDismissTaken = null; } else if (mCancelMessage != null || mDismissMessage != null) { @@ -1306,15 +1319,15 @@ public class Dialog implements DialogInterface, Window.Callback, /** * Sets the callback that will be called if a key is dispatched to the dialog. */ - public void setOnKeyListener(final OnKeyListener onKeyListener) { + public void setOnKeyListener(@Nullable OnKeyListener onKeyListener) { mOnKeyListener = onKeyListener; } private static final class ListenersHandler extends Handler { - private WeakReference<DialogInterface> mDialog; + private final WeakReference<DialogInterface> mDialog; public ListenersHandler(Dialog dialog) { - mDialog = new WeakReference<DialogInterface>(dialog); + mDialog = new WeakReference<>(dialog); } @Override diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index bb3c7191f4e3..c745644adf13 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -1033,11 +1033,11 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * false if it is not. */ public void setUserVisibleHint(boolean isVisibleToUser) { - if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && mFragmentManager != null) { + if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && isAdded()) { mFragmentManager.performPendingDeferredStart(this); } mUserVisibleHint = isVisibleToUser; - mDeferStart = !isVisibleToUser; + mDeferStart = mState < STARTED && !isVisibleToUser; } /** diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 3f2238537d88..4c4f128216f4 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -440,6 +440,11 @@ public class ResourcesManager { compatInfo); classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader(); + if (DEBUG) { + Slog.d(TAG, "createBaseActivityResources activity=" + activityToken + + " with key=" + key); + } + synchronized (this) { final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked( activityToken); @@ -651,6 +656,16 @@ public class ResourcesManager { activityResources.overrideConfig.setToDefaults(); } + if (DEBUG) { + Throwable here = new Throwable(); + here.fillInStackTrace(); + Slog.d(TAG, "updating resources override for activity=" + activityToken + + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig) + + " to newConfig=" + + Configuration.resourceQualifierString(activityResources.overrideConfig), + here); + } + final boolean activityHasOverrideConfig = !activityResources.overrideConfig.equals(Configuration.EMPTY); @@ -692,9 +707,15 @@ public class ResourcesManager { oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId, rebasedOverrideConfig, oldKey.mCompatInfo); + if (DEBUG) { + Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey + + " to newKey=" + newKey); + } + ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey); if (resourcesImpl == null) { resourcesImpl = createResourcesImpl(newKey); + mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl)); } if (resourcesImpl != resources.getImpl()) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 656c7ff01e92..45aa6b44a8f1 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5654,10 +5654,9 @@ public class DevicePolicyManager { } /** - * @hide * Return if this user is a managed profile of another user. An admin can become the profile * owner of a managed profile with {@link #ACTION_PROVISION_MANAGED_PROFILE} and of a managed - * user with {@link #ACTION_PROVISION_MANAGED_USER}. + * user with {@link #createAndManageUser} * @param admin Which profile owner this request is associated with. * @return if this user is a managed profile of another user. */ diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java index 7d698b3ef5a6..01f82e693dee 100644 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -284,6 +284,8 @@ public class BluetoothGattCharacteristic implements Parcelable { out.writeInt(mInstance); out.writeInt(mProperties); out.writeInt(mPermissions); + out.writeInt(mKeySize); + out.writeInt(mWriteType); out.writeTypedList(mDescriptors); } @@ -303,6 +305,8 @@ public class BluetoothGattCharacteristic implements Parcelable { mInstance = in.readInt(); mProperties = in.readInt(); mPermissions = in.readInt(); + mKeySize = in.readInt(); + mWriteType = in.readInt(); mDescriptors = new ArrayList<BluetoothGattDescriptor>(); diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index e355a1c0a539..35437a1fd707 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -144,7 +144,7 @@ public final class BluetoothManager { } /** - * + * * Get a list of devices that match any of the given connection * states. * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e89cbd7091ae..39bc783b53ac 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -130,6 +130,7 @@ public abstract class PackageManager { MATCH_DISABLED_COMPONENTS, MATCH_DISABLED_UNTIL_USED_COMPONENTS, MATCH_SYSTEM_ONLY, + MATCH_FACTORY_ONLY, MATCH_DEBUG_TRIAGED_MISSING, }) @Retention(RetentionPolicy.SOURCE) @@ -415,6 +416,13 @@ public abstract class PackageManager { public static final int MATCH_SYSTEM_ONLY = 0x00100000; /** + * Internal {@link PackageInfo} flag: include only components on the system image. + * This will not return information on any unbundled update to system components. + * @hide + */ + public static final int MATCH_FACTORY_ONLY = 0x00200000; + + /** * Internal flag used to indicate that a system component has done their * homework and verified that they correctly handle packages and components * that come and go over time. In particular: diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 984a960b5308..65e0b9204726 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -113,6 +113,13 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { public static final int PROTECTION_FLAG_PREINSTALLED = 0x400; /** + * Additional flag for {@link #protectionLevel}, corresponding + * to the <code>setup</code> value of + * {@link android.R.attr#protectionLevel}. + */ + public static final int PROTECTION_FLAG_SETUP = 0x800; + + /** * Mask for {@link #protectionLevel}: the basic protection type. */ public static final int PROTECTION_MASK_BASE = 0xf; @@ -226,6 +233,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level&PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) { protLevel += "|preinstalled"; } + if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) { + protLevel += "|setup"; + } return protLevel; } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 63ee8d2e75d3..cc82eb67c275 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -186,7 +186,7 @@ public final class Sensor { * @see #TYPE_LINEAR_ACCELERATION */ public static final String STRING_TYPE_LINEAR_ACCELERATION = - "android.sensor.linear_acceleration"; + "android.sensor.linear_acceleration"; /** * A constant describing a rotation vector sensor type. @@ -229,7 +229,7 @@ public final class Sensor { * @see #TYPE_AMBIENT_TEMPERATURE */ public static final String STRING_TYPE_AMBIENT_TEMPERATURE = - "android.sensor.ambient_temperature"; + "android.sensor.ambient_temperature"; /** * A constant describing an uncalibrated magnetic field sensor type. @@ -254,7 +254,7 @@ public final class Sensor { * @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED */ public static final String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = - "android.sensor.magnetic_field_uncalibrated"; + "android.sensor.magnetic_field_uncalibrated"; /** * A constant describing an uncalibrated rotation vector sensor type. @@ -280,7 +280,7 @@ public final class Sensor { * @see #TYPE_GAME_ROTATION_VECTOR */ public static final String STRING_TYPE_GAME_ROTATION_VECTOR = - "android.sensor.game_rotation_vector"; + "android.sensor.game_rotation_vector"; /** * A constant describing an uncalibrated gyroscope sensor type. @@ -302,7 +302,7 @@ public final class Sensor { * @see #TYPE_GYROSCOPE_UNCALIBRATED */ public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = - "android.sensor.gyroscope_uncalibrated"; + "android.sensor.gyroscope_uncalibrated"; /** * A constant describing a significant motion trigger sensor. @@ -324,7 +324,7 @@ public final class Sensor { * @see #TYPE_SIGNIFICANT_MOTION */ public static final String STRING_TYPE_SIGNIFICANT_MOTION = - "android.sensor.significant_motion"; + "android.sensor.significant_motion"; /** * A constant describing a step detector sensor. @@ -391,7 +391,7 @@ public final class Sensor { * @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR */ public static final String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR = - "android.sensor.geomagnetic_rotation_vector"; + "android.sensor.geomagnetic_rotation_vector"; /** * A constant describing a heart rate monitor. @@ -431,7 +431,7 @@ public final class Sensor { * A constant string describing a wake up tilt detector sensor type. * * @hide - * @see #TYPE_WAKE_UP_TILT_DETECTOR + * @see #TYPE_TILT_DETECTOR */ public static final String SENSOR_STRING_TYPE_TILT_DETECTOR = "android.sensor.tilt_detector"; @@ -495,7 +495,7 @@ public final class Sensor { */ public static final String STRING_TYPE_GLANCE_GESTURE = "android.sensor.glance_gesture"; - /** + /** * A constant describing a pick up sensor. * * A sensor of this type triggers when the device is picked up regardless of wherever it was @@ -514,7 +514,7 @@ public final class Sensor { */ public static final String STRING_TYPE_PICK_UP_GESTURE = "android.sensor.pick_up_gesture"; - /** + /** * A constant describing a wrist tilt gesture sensor. * * A sensor of this type triggers when the device face is tilted towards the user. @@ -553,7 +553,7 @@ public final class Sensor { */ public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation"; - /** + /** * A constant describing a pose sensor with 6 degrees of freedom. * * Similar to {@link #TYPE_ROTATION_VECTOR}, with additional delta @@ -578,7 +578,7 @@ public final class Sensor { */ public static final String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof"; - /** + /** * A constant describing a stationary detect sensor. * * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. @@ -593,7 +593,7 @@ public final class Sensor { */ public static final String STRING_TYPE_STATIONARY_DETECT = "android.sensor.stationary_detect"; - /** + /** * A constant describing a motion detect sensor. * * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. @@ -608,7 +608,7 @@ public final class Sensor { */ public static final String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; - /** + /** * A constant describing a motion detect sensor. * * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. @@ -706,6 +706,14 @@ public final class Sensor { private static final int DATA_INJECTION_MASK = 0x10; private static final int DATA_INJECTION_SHIFT = 4; + // MASK for dynamic sensor (sensor that added during runtime), bit 6. + private static final int DYNAMIC_SENSOR_MASK = 0x20; + private static final int DYNAMIC_SENSOR_SHIFT = 5; + + // MASK for indication bit of sensor additional information support (bit 7). + private static final int ADDITIONAL_INFO_MASK = 0x40; + private static final int ADDITIONAL_INFO_SHIFT = 6; + // TODO(): The following arrays are fragile and error-prone. This needs to be refactored. // Note: This needs to be updated, whenever a new sensor is added. @@ -887,13 +895,37 @@ public final class Sensor { } /** - * @return The type of this sensor as a string. + * @return The UUID of the sensor. If the sensor does not support UUID, the returned value will + * be an all zero UUID; if the sensor's combination of type and name is guaranteed to be unique + * in system, the return value will be an all "F" UUID. + * + * @hide */ + @SystemApi public UUID getUuid() { return mUuid; } /** + * @return The unique id of sensor. Return value of 0 means this sensor does not support UUID; + * return value of -1 means this sensor can be uniquely identified in system by combination of + * its type and name. + */ + public int getId() { + if (mUuid == ALL_0_UUID) { + return 0; + } else if (mUuid == ALL_F_UUID) { + return -1; + } else { + int id = Math.abs(mUuid.hashCode()) + 1; + if (id <= 0) { // catch corner case when hash is Integer.MIN_VALUE and Integer.MAX_VALUE + id = 1; + } + return id; + } + } + + /** * @hide * @return The permission required to access this sensor. If empty, no permission is required. */ @@ -961,6 +993,26 @@ public final class Sensor { } /** + * Returns true if the sensor is a dynamic sensor. + * + * @return <code>true</code> if the sensor is a dynamic sensor (sensor added at runtime). + * @see SensorManager.DynamicSensorCallback + */ + public boolean isDynamicSensor() { + return (mFlags & DYNAMIC_SENSOR_MASK) != 0; + } + + /** + * Returns true if the sensor supports sensor additional information API + * + * @return <code>true</code> if the sensor supports sensor additional information API + * @see SensorAdditionalInfo + */ + public boolean isAdditionalInfoSupported() { + return (mFlags & ADDITIONAL_INFO_MASK) != 0; + } + + /** * Returns true if the sensor supports data injection when the * HAL is set to data injection mode. * @@ -986,6 +1038,10 @@ public final class Sensor { + ", power=" + mPower + ", minDelay=" + mMinDelay + "}"; } + //special UUID hash constant + private final static UUID ALL_0_UUID = new UUID(0,0); + private final static UUID ALL_F_UUID = new UUID(~0L, ~0L); + /** * Sets the Type associated with the sensor. * NOTE: to be used only by native bindings in SensorManager. diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java index 8e5b8a3c1a6c..572a287f53d8 100644 --- a/core/java/android/hardware/SensorAdditionalInfo.java +++ b/core/java/android/hardware/SensorAdditionalInfo.java @@ -27,7 +27,7 @@ import java.lang.annotation.RetentionPolicy; * android.hardware.SensorEventCallback#onSensorAdditionalInfo onSensorAdditionalInfo}. * * @see SensorManager - * @see SensorEventListener3 + * @see SensorEventCallback * @see Sensor * */ @@ -106,7 +106,7 @@ public class SensorAdditionalInfo { * such as accelerometer, gyro, etc. * * Payload: - * floatValues[0..11]: First 3 rows of a homogenous matrix in row major order that captures + * floatValues[0..11]: First 3 rows of a homogeneous matrix in row major order that captures * any linear transformation, including rotation, scaling, shear, shift. */ public static final int TYPE_VEC3_CALIBRATION = 0x10002; diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 35c96f724017..0d96b8e82a5d 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -18,7 +18,7 @@ package android.hardware; /** * This class represents a {@link android.hardware.Sensor Sensor} event and - * holds informations such as the sensor's type, the time-stamp, accuracy and of + * holds information such as the sensor's type, the time-stamp, accuracy and of * course the sensor's {@link SensorEvent#values data}. * * <p> @@ -163,7 +163,7 @@ public class SensorEvent { * </ul> * <p> * Typically the output of the gyroscope is integrated over time to - * calculate a rotation describing the change of angles over the timestep, + * calculate a rotation describing the change of angles over the time step, * for example: * </p> * @@ -173,7 +173,7 @@ public class SensorEvent { * private float timestamp; * * public void onSensorChanged(SensorEvent event) { - * // This timestep's delta rotation to be multiplied by the current rotation + * // This time step's delta rotation to be multiplied by the current rotation * // after computing it from the gyro sample data. * if (timestamp != 0) { * final float dT = (event.timestamp - timestamp) * NS2S; @@ -192,8 +192,8 @@ public class SensorEvent { * axisZ /= omegaMagnitude; * } * - * // Integrate around this axis with the angular speed by the timestep - * // in order to get a delta rotation from this sample over the timestep + * // Integrate around this axis with the angular speed by the time step + * // in order to get a delta rotation from this sample over the time step * // We will convert this axis-angle representation of the delta rotation * // into a quaternion before turning it into the rotation matrix. * float thetaOverTwo = omegaMagnitude * dT / 2.0f; @@ -433,9 +433,9 @@ public class SensorEvent { * Each field is a component of the estimated hard iron calibration. * The values are in micro-Tesla (uT). * </p> - * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanenet + * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanent * magnets on the device. - * Soft iron - These distortions arise due to the interaction with the earth's magentic + * Soft iron - These distortions arise due to the interaction with the earth's magnetic * field. * </p> * <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR}:</h4> @@ -508,14 +508,14 @@ public class SensorEvent { * * * <ul> - * <li> values[0]: x*sin(θ/2) </li> - * <li> values[1]: y*sin(θ/2) </li> - * <li> values[2]: z*sin(θ/2) </li> - * <li> values[3]: cos(θ/2) </li> + * <li> values[0]: x*sin(θ/2) </li> + * <li> values[1]: y*sin(θ/2) </li> + * <li> values[2]: z*sin(θ/2) </li> + * <li> values[3]: cos(θ/2) </li> * * * <li> values[4]: Translation along x axis from an arbitrary origin. </li> - * li> values[5]: Translation along y axis from an arbitrary origin. </li> + * <li> values[5]: Translation along y axis from an arbitrary origin. </li> * <li> values[6]: Translation along z axis from an arbitrary origin. </li> * * <li> values[7]: Delta quaternion rotation x*sin(θ/2) </li> diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 5684aa5cb6fc..a20307a868c7 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -884,7 +884,7 @@ public abstract class SensorManager { * Used for receiving notifications from the SensorManager when dynamic sensors are connected or * disconnected. */ - public static abstract class DynamicSensorConnectionCallback { + public static abstract class DynamicSensorCallback { /** * Called when there is a dynamic sensor being connected to the system. * @@ -902,62 +902,73 @@ public abstract class SensorManager { /** - * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback - * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat + * Add a {@link android.hardware.SensorManager.DynamicSensorCallback + * DynamicSensorCallback} to receive dynamic sensor connection callbacks. Repeat * registration with the already registered callback object will have no additional effect. * * @param callback An object that implements the - * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback - * DynamicSensorConnectionCallback} + * {@link android.hardware.SensorManager.DynamicSensorCallback + * DynamicSensorCallback} * interface for receiving callbacks. - * @see #addDynamicSensorCallback(DynamicSensorConnectionCallback, Handler) + * @see #addDynamicSensorCallback(DynamicSensorCallback, Handler) * * @throws IllegalArgumentException when callback is null. */ - public void registerDynamicSensorCallback(DynamicSensorConnectionCallback callback) { + public void registerDynamicSensorCallback(DynamicSensorCallback callback) { registerDynamicSensorCallback(callback, null); } /** - * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback - * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat + * Add a {@link android.hardware.SensorManager.DynamicSensorCallback + * DynamicSensorCallback} to receive dynamic sensor connection callbacks. Repeat * registration with the already registered callback object will have no additional effect. * * @param callback An object that implements the - * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback - * DynamicSensorConnectionCallback} interface for receiving callbacks. + * {@link android.hardware.SensorManager.DynamicSensorCallback + * DynamicSensorCallback} interface for receiving callbacks. * @param handler The {@link android.os.Handler Handler} the {@link - * android.hardware.SensorManager.DynamicSensorConnectionCallback + * android.hardware.SensorManager.DynamicSensorCallback * sensor connection events} will be delivered to. * * @throws IllegalArgumentException when callback is null. */ public void registerDynamicSensorCallback( - DynamicSensorConnectionCallback callback, Handler handler) { + DynamicSensorCallback callback, Handler handler) { registerDynamicSensorCallbackImpl(callback, handler); } /** - * Remove a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback - * DynamicSensorConnectionCallback} to stop sending dynamic sensor connection events to that + * Remove a {@link android.hardware.SensorManager.DynamicSensorCallback + * DynamicSensorCallback} to stop sending dynamic sensor connection events to that * callback. * * @param callback An object that implements the - * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback - * DynamicSensorConnectionCallback} + * {@link android.hardware.SensorManager.DynamicSensorCallback + * DynamicSensorCallback} * interface for receiving callbacks. */ - public void unregisterDynamicSensorCallback(DynamicSensorConnectionCallback callback) { + public void unregisterDynamicSensorCallback(DynamicSensorCallback callback) { unregisterDynamicSensorCallbackImpl(callback); } + /** + * Tell if dynamic sensor discovery feature is supported by system. + * + * @return <code>true</code> if dynamic sensor discovery is supported, <code>false</code> + * otherwise. + */ + public boolean isDynamicSensorDiscoverySupported() { + List<Sensor> sensors = getSensorList(Sensor.TYPE_DYNAMIC_SENSOR_META); + return sensors.size() > 0; + } + /** @hide */ protected abstract void registerDynamicSensorCallbackImpl( - DynamicSensorConnectionCallback callback, Handler handler); + DynamicSensorCallback callback, Handler handler); /** @hide */ protected abstract void unregisterDynamicSensorCallbackImpl( - DynamicSensorConnectionCallback callback); + DynamicSensorCallback callback); /** * <p> diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 7d903bd89fe4..259ca032918b 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -76,7 +76,7 @@ public class SystemSensorManager extends SensorManager { new HashMap<TriggerEventListener, TriggerEventQueue>(); // Dynamic Sensor callbacks - private HashMap<DynamicSensorConnectionCallback, Handler> + private HashMap<DynamicSensorCallback, Handler> mDynamicSensorCallbacks = new HashMap<>(); private BroadcastReceiver mDynamicSensorBroadcastReceiver; @@ -120,7 +120,7 @@ public class SystemSensorManager extends SensorManager { @Override protected List<Sensor> getFullDynamicSensorList() { // only set up broadcast receiver if the application tries to find dynamic sensors or - // explicitly register a DynamicSensorConnectionCallback + // explicitly register a DynamicSensorCallback setupDynamicSensorBroadcastReceiver(); updateDynamicSensorList(); return mFullDynamicSensorsList; @@ -354,9 +354,9 @@ public class SystemSensorManager extends SensorManager { Handler mainHandler = new Handler(mContext.getMainLooper()); - for (Map.Entry<DynamicSensorConnectionCallback, Handler> entry : + for (Map.Entry<DynamicSensorCallback, Handler> entry : mDynamicSensorCallbacks.entrySet()) { - final DynamicSensorConnectionCallback callback = entry.getKey(); + final DynamicSensorCallback callback = entry.getKey(); Handler handler = entry.getValue() == null ? mainHandler : entry.getValue(); @@ -413,7 +413,7 @@ public class SystemSensorManager extends SensorManager { /** @hide */ protected void registerDynamicSensorCallbackImpl( - DynamicSensorConnectionCallback callback, Handler handler) { + DynamicSensorCallback callback, Handler handler) { if (DEBUG_DYNAMIC_SENSOR) { Log.i(TAG, "DYNS Register dynamic sensor callback"); } @@ -432,7 +432,7 @@ public class SystemSensorManager extends SensorManager { /** @hide */ protected void unregisterDynamicSensorCallbackImpl( - DynamicSensorConnectionCallback callback) { + DynamicSensorCallback callback) { if (DEBUG_DYNAMIC_SENSOR) { Log.i(TAG, "Removing dynamic sensor listerner"); } diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java index 644e29fcaedf..ae44f1d68c3c 100644 --- a/core/java/android/hardware/location/ContextHubInfo.java +++ b/core/java/android/hardware/location/ContextHubInfo.java @@ -37,6 +37,7 @@ public class ContextHubInfo { private float mStoppedPowerDrawMw; private float mSleepPowerDrawMw; private float mPeakPowerDrawMw; + private int mMaxPacketLengthBytes; private int[] mSupportedSensors; @@ -46,6 +47,27 @@ public class ContextHubInfo { } /** + * returns the maximum number of bytes that can be sent per message to the hub + * + * @return int - maximum bytes that can be transmitted in a + * single packet + */ + public int getMaxPacketLengthBytes() { + return mMaxPacketLengthBytes; + } + + /** + * set the context hub unique identifer + * + * @param bytes - Maximum number of bytes per message + * + * @hide + */ + public void setMaxPacketLenBytes(int bytes) { + mMaxPacketLengthBytes = bytes; + } + + /** * get the context hub unique identifer * * @return int - unique system wide identifier @@ -374,4 +396,4 @@ public class ContextHubInfo { return new ContextHubInfo[size]; } }; -} +}
\ No newline at end of file diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 4ddf7673b58c..89edaa9780f6 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -43,23 +43,6 @@ public final class ContextHubManager { private Handler mCallbackHandler; /** - * A special context hub identifier meaning any possible hub on the system. - */ - public static final int ANY_HUB = -1; - /** - * A constant denoting a message to load a a Nano App - */ - public static final int MSG_LOAD_NANO_APP = 1; - /** - * A constant denoting a message to unload a a Nano App - */ - public static final int MSG_UNLOAD_NANO_APP = 2; - /** - * A constant denoting a message to send a message - */ - public static final int MSG_DATA_SEND = 3; - - /** * An interface to receive asynchronous communication from the context hub. */ public abstract static class Callback { @@ -69,7 +52,7 @@ public final class ContextHubManager { * Callback function called on message receipt from context hub. * * @param hubHandle Handle (system-wide unique identifier) of the hub of the message. - * @param nanoAppHandle Handle (unique identifier) for the app that sent the message. + * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message. * @param message The context hub message. * * @see ContextHubMessage @@ -89,7 +72,7 @@ public final class ContextHubManager { try { retVal = getBinder().getContextHubHandles(); } catch (RemoteException e) { - Log.e(TAG, "Could not fetch context hub handles : " + e); + Log.w(TAG, "Could not fetch context hub handles : " + e); } return retVal; } @@ -107,7 +90,7 @@ public final class ContextHubManager { try { retVal = getBinder().getContextHubInfo(hubHandle); } catch (RemoteException e) { - Log.e(TAG, "Could not fetch context hub info :" + e); + Log.w(TAG, "Could not fetch context hub info :" + e); } return retVal; @@ -126,6 +109,7 @@ public final class ContextHubManager { */ public int loadNanoApp(int hubHandle, NanoApp app) { int retVal = -1; + if (app == null) { return retVal; } @@ -133,7 +117,7 @@ public final class ContextHubManager { try { retVal = getBinder().loadNanoApp(hubHandle, app); } catch (RemoteException e) { - Log.e(TAG, "Could not fetch load nanoApp :" + e); + Log.w(TAG, "Could not load nanoApp :" + e); } return retVal; @@ -152,7 +136,7 @@ public final class ContextHubManager { try { retVal = getBinder().unloadNanoApp(nanoAppHandle); } catch (RemoteException e) { - Log.e(TAG, "Could not fetch unload nanoApp :" + e); + Log.w(TAG, "Could not fetch unload nanoApp :" + e); } return retVal; @@ -172,7 +156,7 @@ public final class ContextHubManager { try { retVal = getBinder().getNanoAppInstanceInfo(nanoAppHandle); } catch (RemoteException e) { - Log.e(TAG, "Could not fetch nanoApp info :" + e); + Log.w(TAG, "Could not fetch nanoApp info :" + e); } return retVal; @@ -193,7 +177,7 @@ public final class ContextHubManager { try { retVal = getBinder().findNanoAppOnHub(hubHandle, filter); } catch (RemoteException e) { - Log.e(TAG, "Could not query nanoApp instance :" + e); + Log.w(TAG, "Could not query nanoApp instance :" + e); } return retVal; } @@ -212,10 +196,14 @@ public final class ContextHubManager { public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) { int retVal = -1; + if (message == null || message.getData() == null) { + Log.w(TAG, "null ptr"); + return retVal; + } try { retVal = getBinder().sendMessage(hubHandle, nanoAppHandle, message); } catch (RemoteException e) { - Log.e(TAG, "Could not fetch send message :" + e.toString()); + Log.w(TAG, "Could not send message :" + e.toString()); } return retVal; @@ -247,7 +235,7 @@ public final class ContextHubManager { public int registerCallback(Callback callback, Handler handler) { synchronized(this) { if (mCallback != null) { - Log.e(TAG, "Max number of callbacks reached!"); + Log.w(TAG, "Max number of callbacks reached!"); return -1; } mCallback = callback; @@ -268,7 +256,7 @@ public final class ContextHubManager { public int unregisterCallback(Callback callback) { synchronized(this) { if (callback != mCallback) { - Log.e(TAG, "Cannot recognize callback!"); + Log.w(TAG, "Cannot recognize callback!"); return -1; } @@ -311,11 +299,11 @@ public final class ContextHubManager { try { getBinder().registerCallback(mClientCallback); } catch (RemoteException e) { - Log.e(TAG, "Could not register callback:" + e); + Log.w(TAG, "Could not register callback:" + e); } } else { - Log.d(TAG, "failed to getService"); + Log.w(TAG, "failed to getService"); } } diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java index 954e97dc7fce..bca2ae6d2e8f 100644 --- a/core/java/android/hardware/location/ContextHubMessage.java +++ b/core/java/android/hardware/location/ContextHubMessage.java @@ -16,10 +16,10 @@ package android.hardware.location; - import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; import java.util.Arrays; @@ -32,6 +32,9 @@ public class ContextHubMessage { private int mVersion; private byte[]mData; + private static final String TAG = "ContextHubMessage"; + + /** * Get the message type * @@ -106,9 +109,11 @@ public class ContextHubMessage { private ContextHubMessage(Parcel in) { mType = in.readInt(); mVersion = in.readInt(); - byte[] byteBuffer = new byte[in.readInt()]; - in.readByteArray(byteBuffer); + int bufferLength = in.readInt(); + mData = new byte[bufferLength]; + in.readByteArray(mData); } + public void writeToParcel(Parcel out, int flags) { out.writeInt(mType); out.writeInt(mVersion); diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java index 274babe6a930..b65e24e3b81e 100644 --- a/core/java/android/hardware/location/ContextHubService.java +++ b/core/java/android/hardware/location/ContextHubService.java @@ -29,12 +29,30 @@ import java.util.HashMap; */ public class ContextHubService extends IContextHubService.Stub { + public static final String CONTEXTHUB_SERVICE = "contexthub_service"; + private static final String TAG = "ContextHubService"; private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware"; - public static final String CONTEXTHUB_SERVICE = "contexthub_service"; + + public static final int ANY_HUB = -1; + public static final int MSG_LOAD_NANO_APP = 5; + public static final int MSG_UNLOAD_NANO_APP = 2; + + private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown"; + private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN; + private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN; + private static final int PRE_LOADED_APP_MEM_REQ = 0; + + private static final int MSG_HEADER_SIZE = 4; + private static final int MSG_FIELD_TYPE = 0; + private static final int MSG_FIELD_VERSION = 1; + private static final int MSG_FIELD_HUB_HANDLE = 2; + private static final int MSG_FIELD_APP_INSTANCE = 3; + + private static final int OS_APP_INSTANCE = -1; private final Context mContext; @@ -42,44 +60,27 @@ public class ContextHubService extends IContextHubService.Stub { private ContextHubInfo[] mContextHubInfo; private IContextHubCallback mCallback; + private native int nativeSendMessage(int[] header, byte[] data); + private native ContextHubInfo[] nativeInitialize(); + + public ContextHubService(Context context) { mContext = context; mContextHubInfo = nativeInitialize(); + mNanoAppHash = new HashMap<Integer, NanoAppInstanceInfo>(); for (int i = 0; i < mContextHubInfo.length; i++) { - Log.v(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId() + Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId() + ", name: " + mContextHubInfo[i].getName()); } } - private native int nativeSendMessage(int[] header, byte[] data); - private native ContextHubInfo[] nativeInitialize(); - @Override - public int registerCallback(IContextHubCallback callback) throws RemoteException{ + public int registerCallback(IContextHubCallback callback) throws RemoteException { checkPermissions(); - mCallback = callback; - return 0; - } - - - private int onMessageReceipt(int[] header, byte[] data) { - if (mCallback != null) { - // TODO : Defend against unexpected header sizes - // Add abstraction for magic numbers - // onMessageRecipt should pass the right arguments - ContextHubMessage msg = new ContextHubMessage(header[0], header[1], data); - - try { - mCallback.onMessageReceipt(0, 0, msg); - } catch (Exception e) { - Log.e(TAG, "Exception " + e + " when calling remote callback"); - return -1; - } - } else { - Log.d(TAG, "Message Callback is NULL"); + synchronized(this) { + mCallback = callback; } - return 0; } @@ -118,14 +119,17 @@ public class ContextHubService extends IContextHubService.Stub { } // Call Native interface here - int[] msgHeader = new int[8]; - msgHeader[0] = contextHubHandle; - msgHeader[1] = app.getAppId(); - msgHeader[2] = app.getAppVersion(); - msgHeader[3] = ContextHubManager.MSG_LOAD_NANO_APP; - msgHeader[4] = 0; // Loading hints - - return nativeSendMessage(msgHeader, app.getAppBinary()); + int[] msgHeader = new int[MSG_HEADER_SIZE]; + msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle; + msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE; + msgHeader[MSG_FIELD_VERSION] = 0; + msgHeader[MSG_FIELD_TYPE] = MSG_LOAD_NANO_APP; + + if (nativeSendMessage(msgHeader, app.getAppBinary()) != 0) { + return -1; + } + // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app + return 0; } @Override @@ -137,12 +141,18 @@ public class ContextHubService extends IContextHubService.Stub { } // Call Native interface here - int[] msgHeader = new int[8]; - msgHeader[0] = info.getContexthubId(); - msgHeader[1] = ContextHubManager.MSG_UNLOAD_NANO_APP; - msgHeader[2] = info.getHandle(); + int[] msgHeader = new int[MSG_HEADER_SIZE]; + msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB; + msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE; + msgHeader[MSG_FIELD_VERSION] = 0; + msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP; + + if(nativeSendMessage(msgHeader, null) != 0) { + return -1; + } - return nativeSendMessage(msgHeader, null); + // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app + return 0; } @Override @@ -166,7 +176,7 @@ public class ContextHubService extends IContextHubService.Stub { for(Integer nanoAppInstance : mNanoAppHash.keySet()) { NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance); - if(filter.testMatch(info)){ + if (filter.testMatch(info)){ foundInstances.add(nanoAppInstance); } } @@ -183,12 +193,12 @@ public class ContextHubService extends IContextHubService.Stub { public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException { checkPermissions(); - int[] msgHeader = new int[8]; - msgHeader[0] = ContextHubManager.MSG_DATA_SEND; - msgHeader[1] = hubHandle; - msgHeader[2] = nanoAppHandle; - msgHeader[3] = msg.getMsgType(); - msgHeader[4] = msg.getVersion(); + + int[] msgHeader = new int[MSG_HEADER_SIZE]; + msgHeader[MSG_FIELD_HUB_HANDLE] = hubHandle; + msgHeader[MSG_FIELD_APP_INSTANCE] = nanoAppHandle; + msgHeader[MSG_FIELD_VERSION] = msg.getVersion(); + msgHeader[MSG_FIELD_TYPE] = msg.getMsgType(); return nativeSendMessage(msgHeader, msg.getData()); } @@ -196,5 +206,52 @@ public class ContextHubService extends IContextHubService.Stub { private void checkPermissions() { mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); } -} + private int onMessageReceipt(int[] header, byte[] data) { + if (header == null || data == null || header.length < MSG_HEADER_SIZE) { + return -1; + } + + synchronized(this) { + if (mCallback != null) { + ContextHubMessage msg = new ContextHubMessage(header[MSG_FIELD_TYPE], + header[MSG_FIELD_VERSION], + data); + + try { + mCallback.onMessageReceipt(header[MSG_FIELD_HUB_HANDLE], + header[MSG_FIELD_APP_INSTANCE], + msg); + } catch (Exception e) { + Log.w(TAG, "Exception " + e + " when calling remote callback"); + return -1; + } + } else { + Log.d(TAG, "Message Callback is NULL"); + } + } + + return 0; + } + + private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) { + // App Id encodes vendor & version + NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo(); + + appInfo.setAppId(appId); + appInfo.setAppVersion(appVersion); + appInfo.setName(PRE_LOADED_APP_NAME); + appInfo.setContexthubId(hubHandle); + appInfo.setHandle(appInstanceHandle); + appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER); + appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ); + appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ); + appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ); + + mNanoAppHash.put(appInstanceHandle, appInfo); + Log.d(TAG, "Added app instance " + appInstanceHandle + " with id " + appId + + " version " + appVersion); + + return 0; + } +} diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java index 369f9e44e8d9..8db70e9c53f1 100644 --- a/core/java/android/hardware/location/NanoAppFilter.java +++ b/core/java/android/hardware/location/NanoAppFilter.java @@ -20,6 +20,7 @@ package android.hardware.location; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; /** * @hide @@ -27,6 +28,8 @@ import android.os.Parcelable; @SystemApi public class NanoAppFilter { + private static final String TAG = "NanoAppFilter"; + // The appId, can be set to APP_ID_ANY private long mAppId; @@ -54,6 +57,10 @@ public class NanoAppFilter { * If this flag is set, only versions strictly less than the version specified shall match. */ public static final int FLAGS_VERSION_LESS_THAN = 4; + /** + * If this flag is set, only versions strictly equal to the + * version specified shall match. + */ public static final int FLAGS_VERSION_STRICTLY_EQUAL = 8; /** @@ -117,14 +124,9 @@ public class NanoAppFilter { * @return true if this is a match, false otherwise */ public boolean testMatch(NanoAppInstanceInfo info) { - if ((mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) && + return (mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) && (mAppId == APP_ANY || info.getAppId() == mAppId) && - // (mAppIdVendorMask == VENDOR_ANY) TODO : Expose Vendor mask cleanly - (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()))) { - return true; - } else { - return false; - } + (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion())); } public static final Parcelable.Creator<NanoAppFilter> CREATOR diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java index ac62919b7931..977f645db199 100644 --- a/core/java/android/hardware/location/NanoAppInstanceInfo.java +++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java @@ -29,7 +29,7 @@ public class NanoAppInstanceInfo { private String mPublisher; private String mName; - private int mAppId; + private long mAppId; private int mAppVersion; private int mNeededReadMemBytes; @@ -59,6 +59,8 @@ public class NanoAppInstanceInfo { * set the publisher name for the app * * @param publisher - name of the publisher + * + * @hide */ public void setPublisher(String publisher) { mPublisher = publisher; @@ -77,6 +79,8 @@ public class NanoAppInstanceInfo { * set the name of the app * * @param name - name of the app + * + * @hide */ public void setName(String name) { mName = name; @@ -87,7 +91,7 @@ public class NanoAppInstanceInfo { * * @return int - application identifier */ - public int getAppId() { + public long getAppId() { return mAppId; } @@ -95,8 +99,10 @@ public class NanoAppInstanceInfo { * Set the application identifier * * @param appId - application identifier + * + * @hide */ - public void setAppId(int appId) { + public void setAppId(long appId) { mAppId = appId; } @@ -113,6 +119,8 @@ public class NanoAppInstanceInfo { * Set the application version * * @param appVersion - version of the app + * + * @hide */ public void setAppVersion(int appVersion) { mAppVersion = appVersion; @@ -131,6 +139,8 @@ public class NanoAppInstanceInfo { * Set the read memory needed by the app * * @param neededReadMemBytes - readable Memory needed in bytes + * + * @hide */ public void setNeededReadMemBytes(int neededReadMemBytes) { mNeededReadMemBytes = neededReadMemBytes; @@ -150,6 +160,8 @@ public class NanoAppInstanceInfo { * * @param neededWriteMemBytes - writable memory needed by the * app + * + * @hide */ public void setNeededWriteMemBytes(int neededWriteMemBytes) { mNeededWriteMemBytes = neededWriteMemBytes; @@ -169,6 +181,8 @@ public class NanoAppInstanceInfo { * * @param neededExecMemBytes - executable memory needed by the * app + * + * @hide */ public void setNeededExecMemBytes(int neededExecMemBytes) { mNeededExecMemBytes = neededExecMemBytes; @@ -187,6 +201,8 @@ public class NanoAppInstanceInfo { * set the sensors needed by this app * * @param neededSensors - all the sensors needed by this app + * + * @hide */ public void setNeededSensors(int[] neededSensors) { mNeededSensors = neededSensors; @@ -206,6 +222,8 @@ public class NanoAppInstanceInfo { * * @param outputEvents - the events that may be generated by * this app + * + * @hide */ public void setOutputEvents(int[] outputEvents) { mOutputEvents = outputEvents; @@ -224,6 +242,8 @@ public class NanoAppInstanceInfo { * set the context hub identifier * * @param contexthubId - system wide unique identifier + * + * @hide */ public void setContexthubId(int contexthubId) { mContexthubId = contexthubId; @@ -242,6 +262,8 @@ public class NanoAppInstanceInfo { * set the handle for an app instance * * @param handle - handle to this instance + * + * @hide */ public void setHandle(int handle) { mHandle = handle; @@ -252,7 +274,7 @@ public class NanoAppInstanceInfo { mPublisher = in.readString(); mName = in.readString(); - mAppId = in.readInt(); + mAppId = in.readLong(); mAppVersion = in.readInt(); mNeededReadMemBytes = in.readInt(); mNeededWriteMemBytes = in.readInt(); @@ -274,7 +296,7 @@ public class NanoAppInstanceInfo { public void writeToParcel(Parcel out, int flags) { out.writeString(mPublisher); out.writeString(mName); - out.writeInt(mAppId); + out.writeLong(mAppId); out.writeInt(mAppVersion); out.writeInt(mContexthubId); out.writeInt(mNeededReadMemBytes); @@ -286,7 +308,6 @@ public class NanoAppInstanceInfo { out.writeInt(mOutputEvents.length); out.writeIntArray(mOutputEvents); - } public static final Parcelable.Creator<NanoAppInstanceInfo> CREATOR diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 97bd5d284c71..8c5f60395ca9 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -40,6 +40,9 @@ public class DhcpResults extends StaticIpConfiguration { public int leaseDuration; + /** Link MTU option. 0 means unset. */ + public int mtu; + public DhcpResults() { super(); } @@ -57,19 +60,7 @@ public class DhcpResults extends StaticIpConfiguration { serverAddress = source.serverAddress; vendorInfo = source.vendorInfo; leaseDuration = source.leaseDuration; - } - } - - /** - * Updates the DHCP fields that need to be retained from - * original DHCP request if the current renewal shows them - * being empty. - */ - public void updateFromDhcpRequest(DhcpResults orig) { - if (orig == null) return; - if (gateway == null) gateway = orig.gateway; - if (dnsServers.size() == 0) { - dnsServers.addAll(orig.dnsServers); + mtu = source.mtu; } } @@ -89,6 +80,7 @@ public class DhcpResults extends StaticIpConfiguration { super.clear(); vendorInfo = null; leaseDuration = 0; + mtu = 0; } @Override @@ -98,6 +90,7 @@ public class DhcpResults extends StaticIpConfiguration { str.append(" DHCP server ").append(serverAddress); str.append(" Vendor info ").append(vendorInfo); str.append(" lease ").append(leaseDuration).append(" seconds"); + if (mtu != 0) str.append(" MTU ").append(mtu); return str.toString(); } @@ -113,7 +106,8 @@ public class DhcpResults extends StaticIpConfiguration { return super.equals((StaticIpConfiguration) obj) && Objects.equals(serverAddress, target.serverAddress) && Objects.equals(vendorInfo, target.vendorInfo) && - leaseDuration == target.leaseDuration; + leaseDuration == target.leaseDuration && + mtu == target.mtu; } /** Implement the Parcelable interface */ @@ -134,6 +128,7 @@ public class DhcpResults extends StaticIpConfiguration { public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(leaseDuration); + dest.writeInt(mtu); NetworkUtils.parcelInetAddress(dest, serverAddress, flags); dest.writeString(vendorInfo); } @@ -141,6 +136,7 @@ public class DhcpResults extends StaticIpConfiguration { private static void readFromParcel(DhcpResults dhcpResults, Parcel in) { StaticIpConfiguration.readFromParcel(dhcpResults, in); dhcpResults.leaseDuration = in.readInt(); + dhcpResults.mtu = in.readInt(); dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in); dhcpResults.vendorInfo = in.readString(); } diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 05dd48b12e55..11280749f05b 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -208,6 +208,18 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Removes any entry with the given key from the mapping of this Bundle. + * + * @param key a String key + */ + public void remove(String key) { + super.remove(key); + if ((mFlags & FLAG_HAS_FDS) != 0) { + mFlags &= ~FLAG_HAS_FDS_KNOWN; + } + } + + /** * Inserts all mappings from the given Bundle into this Bundle. * * @param bundle a Bundle @@ -288,6 +300,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { if (fdFound) { mFlags |= FLAG_HAS_FDS; + } else { + mFlags &= ~FLAG_HAS_FDS; } mFlags |= FLAG_HAS_FDS_KNOWN; } @@ -315,6 +329,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { mMap.removeAt(i); } } + mFlags |= FLAG_HAS_FDS_KNOWN; + mFlags &= ~FLAG_HAS_FDS; } /** diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index e9c196d0cdad..721c94ef1c7d 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources.NotFoundException; @@ -31,6 +32,7 @@ import android.util.ArraySet; import android.util.Log; import com.android.internal.R; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -49,7 +51,7 @@ public final class PrintAttributes implements Parcelable { @IntDef(flag = true, value = { COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR }) - public @interface ColorMode { + @interface ColorMode { } /** Color mode: Monochrome color scheme, for example one color is used. */ public static final int COLOR_MODE_MONOCHROME = 1 << 0; @@ -64,7 +66,7 @@ public final class PrintAttributes implements Parcelable { @IntDef(flag = true, value = { DUPLEX_MODE_NONE, DUPLEX_MODE_LONG_EDGE, DUPLEX_MODE_SHORT_EDGE }) - public @interface DuplexMode { + @interface DuplexMode { } /** Duplex mode: No duplexing. */ public static final int DUPLEX_MODE_NONE = 1 << 0; @@ -76,23 +78,29 @@ public final class PrintAttributes implements Parcelable { private static final int VALID_DUPLEX_MODES = DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE; - private MediaSize mMediaSize; - private Resolution mResolution; - private Margins mMinMargins; + private @Nullable MediaSize mMediaSize; + private @Nullable Resolution mResolution; + private @Nullable Margins mMinMargins; - private int mColorMode; - private int mDuplexMode; + private @IntRange(from = 0) int mColorMode; + private @IntRange(from = 0) int mDuplexMode; PrintAttributes() { /* hide constructor */ } private PrintAttributes(@NonNull Parcel parcel) { - mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null; - mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null; - mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null; + mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null; + mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null; + mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null; mColorMode = parcel.readInt(); + if (mColorMode != 0) { + enforceValidColorMode(mColorMode); + } mDuplexMode = parcel.readInt(); + if (mDuplexMode != 0) { + enforceValidDuplexMode(mDuplexMode); + } } /** @@ -179,7 +187,7 @@ public final class PrintAttributes implements Parcelable { * @see #COLOR_MODE_COLOR * @see #COLOR_MODE_MONOCHROME */ - public @ColorMode int getColorMode() { + public @IntRange(from = 0) int getColorMode() { return mColorMode; } @@ -214,13 +222,13 @@ public final class PrintAttributes implements Parcelable { /** * Gets the duplex mode. * - * @return The duplex mode. + * @return The duplex mode or zero if not set. * * @see #DUPLEX_MODE_NONE * @see #DUPLEX_MODE_LONG_EDGE * @see #DUPLEX_MODE_SHORT_EDGE */ - public @DuplexMode int getDuplexMode() { + public @IntRange(from = 0) int getDuplexMode() { return mDuplexMode; } @@ -448,7 +456,7 @@ public final class PrintAttributes implements Parcelable { private static final String LOG_TAG = "MediaSize"; private static final Map<String, MediaSize> sIdToMediaSizeMap = - new ArrayMap<String, MediaSize>(); + new ArrayMap<>(); /** * Unknown media size in portrait mode. @@ -781,15 +789,15 @@ public final class PrintAttributes implements Parcelable { new MediaSize("JPN_YOU4", "android", R.string.mediasize_japanese_you4, 4134, 9252); - private final String mId; + private final @NonNull String mId; /**@hide */ - public final String mLabel; + public final @NonNull String mLabel; /**@hide */ - public final String mPackageName; + public final @Nullable String mPackageName; /**@hide */ - public final int mLabelResId; - private final int mWidthMils; - private final int mHeightMils; + public final @StringRes int mLabelResId; + private final @IntRange(from = 1) int mWidthMils; + private final @IntRange(from = 1) int mHeightMils; /** * Creates a new instance. @@ -808,29 +816,7 @@ public final class PrintAttributes implements Parcelable { */ public MediaSize(String id, String packageName, int labelResId, int widthMils, int heightMils) { - if (TextUtils.isEmpty(id)) { - throw new IllegalArgumentException("id cannot be empty."); - } - if (TextUtils.isEmpty(packageName)) { - throw new IllegalArgumentException("packageName cannot be empty."); - } - if (labelResId <= 0) { - throw new IllegalArgumentException("labelResId must be greater than zero."); - } - if (widthMils <= 0) { - throw new IllegalArgumentException("widthMils " - + "cannot be less than or equal to zero."); - } - if (heightMils <= 0) { - throw new IllegalArgumentException("heightMils " - + "cannot be less than or euqual to zero."); - } - mPackageName = packageName; - mId = id; - mLabelResId = labelResId; - mWidthMils = widthMils; - mHeightMils = heightMils; - mLabel = null; + this(id, null, packageName, widthMils, heightMils, labelResId); // Build this mapping only for predefined media sizes. sIdToMediaSizeMap.put(mId, this); @@ -851,26 +837,7 @@ public final class PrintAttributes implements Parcelable { */ public MediaSize(@NonNull String id, @NonNull String label, @IntRange(from = 1) int widthMils, @IntRange(from = 1) int heightMils) { - if (TextUtils.isEmpty(id)) { - throw new IllegalArgumentException("id cannot be empty."); - } - if (TextUtils.isEmpty(label)) { - throw new IllegalArgumentException("label cannot be empty."); - } - if (widthMils <= 0) { - throw new IllegalArgumentException("widthMils " - + "cannot be less than or equal to zero."); - } - if (heightMils <= 0) { - throw new IllegalArgumentException("heightMils " - + "cannot be less than or euqual to zero."); - } - mId = id; - mLabel = label; - mWidthMils = widthMils; - mHeightMils = heightMils; - mLabelResId = 0; - mPackageName = null; + this(id, label, null, widthMils, heightMils, 0); } /** @@ -890,15 +857,37 @@ public final class PrintAttributes implements Parcelable { return definedMediaSizes; } - /** @hide */ - public MediaSize(String id, String label, String packageName, - int widthMils, int heightMils, int labelResId) { + /** + * Creates a new instance. + * + * @param id The unique media size id. It is unique amongst other media sizes + * supported by the printer. + * @param label The <strong>localized</strong> human readable label. + * @param packageName The name of the creating package. + * @param widthMils The width in mils (thousands of an inch). + * @param heightMils The height in mils (thousands of an inch). + * @param labelResId The resource if of a human readable label. + * + * @throws IllegalArgumentException If the id is empty or the label is unset + * or the widthMils is less than or equal to zero or the heightMils is less + * than or equal to zero. + * + * @hide + */ + public MediaSize(String id, String label, String packageName, int widthMils, int heightMils, + int labelResId) { mPackageName = packageName; - mId = id; + mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty."); mLabelResId = labelResId; - mWidthMils = widthMils; - mHeightMils = heightMils; + mWidthMils = Preconditions.checkArgumentPositive(widthMils, "widthMils cannot be " + + "less than or equal to zero."); + mHeightMils = Preconditions.checkArgumentPositive(heightMils, "heightMils cannot be " + + "less than or equal to zero."); mLabel = label; + + // The label has to be either a string ot a StringRes + Preconditions.checkArgument(!TextUtils.isEmpty(label) != + (!TextUtils.isEmpty(packageName) && labelResId != 0), "label cannot be empty."); } /** @@ -926,10 +915,7 @@ public final class PrintAttributes implements Parcelable { try { return packageManager.getResourcesForApplication( mPackageName).getString(mLabelResId); - } catch (NotFoundException nfe) { - Log.w(LOG_TAG, "Could not load resouce" + mLabelResId - + " from package " + mPackageName); - } catch (NameNotFoundException nnfee) { + } catch (NotFoundException | NameNotFoundException e) { Log.w(LOG_TAG, "Could not load resouce" + mLabelResId + " from package " + mPackageName); } @@ -1084,10 +1070,10 @@ public final class PrintAttributes implements Parcelable { * the one with 300 DPI resolution. */ public static final class Resolution { - private final String mId; - private final String mLabel; - private final int mHorizontalDpi; - private final int mVerticalDpi; + private final @NonNull String mId; + private final @NonNull String mLabel; + private final @IntRange(from = 1) int mHorizontalDpi; + private final @IntRange(from = 1) int mVerticalDpi; /** * Creates a new instance. @@ -1244,8 +1230,7 @@ public final class PrintAttributes implements Parcelable { * @param rightMils The right margin in mils (thousands of an inch). * @param bottomMils The bottom margin in mils (thousands of an inch). */ - public Margins(@IntRange(from = 0) int leftMils, @IntRange(from = 0) int topMils, - @IntRange(from = 0) int rightMils, @IntRange(from = 0) int bottomMils) { + public Margins(int leftMils, int topMils, int rightMils, int bottomMils) { mTopMils = topMils; mLeftMils = leftMils; mRightMils = rightMils; @@ -1257,7 +1242,7 @@ public final class PrintAttributes implements Parcelable { * * @return The left margin. */ - public @IntRange(from = 0) int getLeftMils() { + public int getLeftMils() { return mLeftMils; } @@ -1266,7 +1251,7 @@ public final class PrintAttributes implements Parcelable { * * @return The top margin. */ - public @IntRange(from = 0) int getTopMils() { + public int getTopMils() { return mTopMils; } @@ -1275,7 +1260,7 @@ public final class PrintAttributes implements Parcelable { * * @return The right margin. */ - public @IntRange(from = 0) int getRightMils() { + public int getRightMils() { return mRightMils; } @@ -1284,7 +1269,7 @@ public final class PrintAttributes implements Parcelable { * * @return The bottom margin. */ - public @IntRange(from = 0) int getBottomMils() { + public int getBottomMils() { return mBottomMils; } diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index db3b6f47af43..bec6f290dbdf 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -114,8 +115,8 @@ public final class PrintDocumentInfo implements Parcelable { */ public static final int CONTENT_TYPE_PHOTO = 1; - private String mName; - private int mPageCount; + private @NonNull String mName; + private @IntRange(from = -1) int mPageCount; private int mContentType; private long mDataSize; @@ -144,10 +145,11 @@ public final class PrintDocumentInfo implements Parcelable { * @param parcel Data from which to initialize. */ private PrintDocumentInfo(Parcel parcel) { - mName = parcel.readString(); + mName = Preconditions.checkStringNotEmpty(parcel.readString()); mPageCount = parcel.readInt(); + Preconditions.checkArgument(mPageCount == PAGE_COUNT_UNKNOWN || mPageCount > 0); mContentType = parcel.readInt(); - mDataSize = parcel.readLong(); + mDataSize = Preconditions.checkArgumentNonnegative(parcel.readLong()); } /** @@ -180,7 +182,7 @@ public final class PrintDocumentInfo implements Parcelable { * @see #CONTENT_TYPE_DOCUMENT * @see #CONTENT_TYPE_PHOTO */ - public @ContentType int getContentType() { + public int getContentType() { return mContentType; } @@ -262,13 +264,13 @@ public final class PrintDocumentInfo implements Parcelable { builder.append("PrintDocumentInfo{"); builder.append("name=").append(mName); builder.append(", pageCount=").append(mPageCount); - builder.append(", contentType=").append(contentTyepToString(mContentType)); + builder.append(", contentType=").append(contentTypeToString(mContentType)); builder.append(", dataSize=").append(mDataSize); builder.append("}"); return builder.toString(); } - private String contentTyepToString(int contentType) { + private String contentTypeToString(int contentType) { switch (contentType) { case CONTENT_TYPE_DOCUMENT: { return "CONTENT_TYPE_DOCUMENT"; diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java index d13879ba6914..01c23f6734b0 100644 --- a/core/java/android/print/PrinterCapabilitiesInfo.java +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -24,11 +24,13 @@ import android.print.PrintAttributes.DuplexMode; import android.print.PrintAttributes.Margins; import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Resolution; +import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.IntConsumer; /** * This class represents the capabilities of a printer. Instances @@ -55,9 +57,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable { private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0); - private Margins mMinMargins = DEFAULT_MARGINS; - private List<MediaSize> mMediaSizes; - private List<Resolution> mResolutions; + private @NonNull Margins mMinMargins = DEFAULT_MARGINS; + private @NonNull List<MediaSize> mMediaSizes; + private @NonNull List<Resolution> mResolutions; private int mColorModes; private int mDuplexModes; @@ -205,15 +207,37 @@ public final class PrinterCapabilitiesInfo implements Parcelable { return builder.build(); } + /** + * Call enforceSingle for each bit in the mask. + * + * @param mask The mask + * @param enforceSingle The function to call + */ + private static void enforceValidMask(int mask, IntConsumer enforceSingle) { + int current = mask; + while (current > 0) { + final int currentMode = (1 << Integer.numberOfTrailingZeros(current)); + current &= ~currentMode; + enforceSingle.accept(currentMode); + } + } + private PrinterCapabilitiesInfo(Parcel parcel) { - mMinMargins = readMargins(parcel); + mMinMargins = Preconditions.checkNotNull(readMargins(parcel)); readMediaSizes(parcel); readResolutions(parcel); mColorModes = parcel.readInt(); + enforceValidMask(mColorModes, + (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode)); + mDuplexModes = parcel.readInt(); + enforceValidMask(mDuplexModes, + (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode)); readDefaults(parcel); + Preconditions.checkArgument(mMediaSizes.size() > mDefaults[PROPERTY_MEDIA_SIZE]); + Preconditions.checkArgument(mResolutions.size() > mDefaults[PROPERTY_RESOLUTION]); } @Override @@ -537,12 +561,8 @@ public final class PrinterCapabilitiesInfo implements Parcelable { */ public @NonNull Builder setColorModes(@ColorMode int colorModes, @ColorMode int defaultColorMode) { - int currentModes = colorModes; - while (currentModes > 0) { - final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes)); - currentModes &= ~currentMode; - PrintAttributes.enforceValidColorMode(currentMode); - } + enforceValidMask(colorModes, + (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode)); PrintAttributes.enforceValidColorMode(defaultColorMode); mPrototype.mColorModes = colorModes; mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode; @@ -568,12 +588,8 @@ public final class PrinterCapabilitiesInfo implements Parcelable { */ public @NonNull Builder setDuplexModes(@DuplexMode int duplexModes, @DuplexMode int defaultDuplexMode) { - int currentModes = duplexModes; - while (currentModes > 0) { - final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes)); - currentModes &= ~currentMode; - PrintAttributes.enforceValidDuplexMode(currentMode); - } + enforceValidMask(duplexModes, + (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode)); PrintAttributes.enforceValidDuplexMode(defaultDuplexMode); mPrototype.mDuplexModes = duplexModes; mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode; diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 7af0b05f9cc5..0557d138eb74 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -35,7 +35,6 @@ import android.util.Slog; import android.view.ActionMode; import android.view.Display; import android.view.KeyEvent; -import android.view.KeyboardShortcutGroup; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 5bcf1027f384..4ba97d5f8bea 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -180,7 +180,12 @@ interface IWindowManager // caller must call setNewConfiguration() sometime later. Configuration updateOrientationFromAppTokens(in Configuration currentConfig, IBinder freezeThisOneIfNeeded); - void setNewConfiguration(in Configuration config); + // Notify window manager of the new configuration. Returns an array of stack ids that's + // affected by the update, ActivityManager should resize these stacks. + int[] setNewConfiguration(in Configuration config); + + // Retrieves the new bounds after the configuration update evaluated by window manager. + Rect getBoundsForNewConfiguration(int stackId); void startFreezingScreen(int exitAnim, int enterAnim); void stopFreezingScreen(); diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java index c2bd347687a6..eee925df5038 100644 --- a/core/java/android/view/KeyboardShortcutInfo.java +++ b/core/java/android/view/KeyboardShortcutInfo.java @@ -51,7 +51,7 @@ public final class KeyboardShortcutInfo implements Parcelable { mLabel = label; mIcon = icon; mBaseCharacter = MIN_VALUE; - checkArgument(keycode > KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode()); + checkArgument(keycode >= KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode()); mKeycode = keycode; mModifiers = modifiers; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3ee6a8d1a179..784164d243f1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -18079,13 +18079,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * to clear the previous drawable. setVisible first while we still have the callback set. */ if (mBackground != null) { - // It's possible for this method to be invoked from the View constructor before - // subclass constructors have run. Drawables can and should trigger invalidations - // and other activity with their callback on visibility changes, which shouldn't - // happen before subclass constructors finish. However, we won't have set the - // drawable as visible until the view becomes attached. This guard below keeps - // multiple calls to this method from constructors from causing issues. - if (mBackground.isVisible()) { + if (isAttachedToWindow()) { mBackground.setVisible(false, false); } mBackground.setCallback(null); @@ -18320,13 +18314,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (mForegroundInfo.mDrawable != null) { - // It's possible for this method to be invoked from the View constructor before - // subclass constructors have run. Drawables can and should trigger invalidations - // and other activity with their callback on visibility changes, which shouldn't - // happen before subclass constructors finish. However, we won't have set the - // drawable as visible until the view becomes attached. This guard below keeps - // multiple calls to this method from constructors from causing issues. - if (mForegroundInfo.mDrawable.isVisible()) { + if (isAttachedToWindow()) { mForegroundInfo.mDrawable.setVisible(false, false); } mForegroundInfo.mDrawable.setCallback(null); diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index d884f199dd86..cc496dc72d7c 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -573,8 +573,12 @@ public final class WebViewFactory { intent.getDataString().substring("package:".length())); } - private static IWebViewUpdateService getUpdateService() { - return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); + private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate"; + + /** @hide */ + public static IWebViewUpdateService getUpdateService() { + return IWebViewUpdateService.Stub.asInterface( + ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); } private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve); diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java index d106dba4dd3a..5d091c91abfb 100644 --- a/core/java/android/webkit/WebViewProviderInfo.java +++ b/core/java/android/webkit/WebViewProviderInfo.java @@ -16,12 +16,16 @@ package android.webkit; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.util.Arrays; -/** @hide */ +/** + * @hide + */ +@SystemApi public final class WebViewProviderInfo implements Parcelable { public WebViewProviderInfo(String packageName, String description, diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java new file mode 100644 index 000000000000..4e83d8834062 --- /dev/null +++ b/core/java/android/webkit/WebViewUpdateService.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.annotation.SystemApi; +import android.os.RemoteException; + +/** + * @hide + */ +@SystemApi +public final class WebViewUpdateService { + + private WebViewUpdateService () {} + + /** + * Fetch all packages that could potentially implement WebView. + */ + public static WebViewProviderInfo[] getAllWebViewPackages() { + try { + return getUpdateService().getAllWebViewPackages(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** + * Fetch all packages that could potentially implement WebView and are currently valid. + */ + public static WebViewProviderInfo[] getValidWebViewPackages() { + try { + return getUpdateService().getValidWebViewPackages(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** + * Used by DevelopmentSetting to get the name of the WebView provider currently in use. + */ + public static String getCurrentWebViewPackageName() { + try { + return getUpdateService().getCurrentWebViewPackageName(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + private static IWebViewUpdateService getUpdateService() { + return WebViewFactory.getUpdateService(); + } +} diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index 027f6d6f2727..9d228cf667b1 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -20,6 +20,7 @@ import android.annotation.ArrayRes; import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.util.Log; @@ -61,17 +62,13 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp private final LayoutInflater mInflater; - /** - * Contains the list of objects that represent the data of this ArrayAdapter. - * The content of this list is referred to as "the array" in the documentation. - */ - private List<T> mObjects; + private final Context mContext; /** * The resource indicating what views to inflate to display the content of this * array adapter. */ - private int mResource; + private final int mResource; /** * The resource indicating what views to inflate to display the content of this @@ -80,7 +77,13 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp private int mDropDownResource; /** - * If the inflated resource is not a TextView, {@link #mFieldId} is used to find + * Contains the list of objects that represent the data of this ArrayAdapter. + * The content of this list is referred to as "the array" in the documentation. + */ + private List<T> mObjects; + + /** + * If the inflated resource is not a TextView, {@code mFieldId} is used to find * a TextView inside the inflated views hierarchy. This field must contain the * identifier that matches the one defined in the resource file. */ @@ -92,8 +95,6 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp */ private boolean mNotifyOnChange = true; - private Context mContext; - // A copy of the original mObjects array, initialized from and then used instead as soon as // the mFilter ArrayFilter is used. mObjects will then only contain the filtered values. private ArrayList<T> mOriginalValues; @@ -109,8 +110,8 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * @param resource The resource ID for a layout file containing a TextView to use when * instantiating views. */ - public ArrayAdapter(Context context, @LayoutRes int resource) { - this(context, resource, 0, new ArrayList<T>()); + public ArrayAdapter(@NonNull Context context, @LayoutRes int resource) { + this(context, resource, 0, new ArrayList<>()); } /** @@ -121,8 +122,9 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * instantiating views. * @param textViewResourceId The id of the TextView within the layout resource to be populated */ - public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId) { - this(context, resource, textViewResourceId, new ArrayList<T>()); + public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, + @IdRes int textViewResourceId) { + this(context, resource, textViewResourceId, new ArrayList<>()); } /** @@ -133,7 +135,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * instantiating views. * @param objects The objects to represent in the ListView. */ - public ArrayAdapter(Context context, @LayoutRes int resource, @NonNull T[] objects) { + public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) { this(context, resource, 0, Arrays.asList(objects)); } @@ -146,8 +148,8 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * @param textViewResourceId The id of the TextView within the layout resource to be populated * @param objects The objects to represent in the ListView. */ - public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId, - @NonNull T[] objects) { + public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, + @IdRes int textViewResourceId, @NonNull T[] objects) { this(context, resource, textViewResourceId, Arrays.asList(objects)); } @@ -159,7 +161,8 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * instantiating views. * @param objects The objects to represent in the ListView. */ - public ArrayAdapter(Context context, @LayoutRes int resource, @NonNull List<T> objects) { + public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, + @NonNull List<T> objects) { this(context, resource, 0, objects); } @@ -172,8 +175,8 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * @param textViewResourceId The id of the TextView within the layout resource to be populated * @param objects The objects to represent in the ListView. */ - public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId, - @NonNull List<T> objects) { + public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, + @IdRes int textViewResourceId, @NonNull List<T> objects) { mContext = context; mInflater = LayoutInflater.from(context); mResource = mDropDownResource = resource; @@ -186,7 +189,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * * @param object The object to add at the end of the array. */ - public void add(T object) { + public void add(@Nullable T object) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.add(object); @@ -201,8 +204,17 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * Adds the specified Collection at the end of the array. * * @param collection The Collection to add at the end of the array. - */ - public void addAll(Collection<? extends T> collection) { + * @throws UnsupportedOperationException if the <tt>addAll</tt> operation + * is not supported by this list + * @throws ClassCastException if the class of an element of the specified + * collection prevents it from being added to this list + * @throws NullPointerException if the specified collection contains one + * or more null elements and this list does not permit null + * elements, or if the specified collection is null + * @throws IllegalArgumentException if some property of an element of the + * specified collection prevents it from being added to this list + */ + public void addAll(@NonNull Collection<? extends T> collection) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.addAll(collection); @@ -235,7 +247,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * @param object The object to insert into the array. * @param index The index at which the object must be inserted. */ - public void insert(T object, int index) { + public void insert(@Nullable T object, int index) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.add(index, object); @@ -251,7 +263,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * * @param object The object to remove. */ - public void remove(T object) { + public void remove(@Nullable T object) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.remove(object); @@ -282,7 +294,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * @param comparator The comparator used to sort the objects contained * in this adapter. */ - public void sort(Comparator<? super T> comparator) { + public void sort(@NonNull Comparator<? super T> comparator) { synchronized (mLock) { if (mOriginalValues != null) { Collections.sort(mOriginalValues, comparator); @@ -293,9 +305,6 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp if (mNotifyOnChange) notifyDataSetChanged(); } - /** - * {@inheritDoc} - */ @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); @@ -326,21 +335,17 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * * @return The Context associated with this adapter. */ - public Context getContext() { + public @NonNull Context getContext() { return mContext; } - /** - * {@inheritDoc} - */ + @Override public int getCount() { return mObjects.size(); } - /** - * {@inheritDoc} - */ - public T getItem(int position) { + @Override + public @Nullable T getItem(int position) { return mObjects.get(position); } @@ -351,28 +356,25 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * * @return The position of the specified item. */ - public int getPosition(T item) { + public int getPosition(@Nullable T item) { return mObjects.indexOf(item); } - /** - * {@inheritDoc} - */ + @Override public long getItemId(int position) { return position; } - /** - * {@inheritDoc} - */ - public View getView(int position, View convertView, ViewGroup parent) { + @Override + public @NonNull View getView(int position, @Nullable View convertView, + @NonNull ViewGroup parent) { return createViewFromResource(mInflater, position, convertView, parent, mResource); } - private View createViewFromResource(LayoutInflater inflater, int position, View convertView, - ViewGroup parent, int resource) { - View view; - TextView text; + private @NonNull View createViewFromResource(@NonNull LayoutInflater inflater, int position, + @Nullable View convertView, @NonNull ViewGroup parent, int resource) { + final View view; + final TextView text; if (convertView == null) { view = inflater.inflate(resource, parent, false); @@ -387,6 +389,12 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp } else { // Otherwise, find the TextView field within the layout text = (TextView) view.findViewById(mFieldId); + + if (text == null) { + throw new RuntimeException("Failed to find view with ID " + + mContext.getResources().getResourceName(mFieldId) + + " in item layout"); + } } } catch (ClassCastException e) { Log.e("ArrayAdapter", "You must supply a resource ID for a TextView"); @@ -394,9 +402,9 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp "ArrayAdapter requires the resource ID to be a TextView", e); } - T item = getItem(position); + final T item = getItem(position); if (item instanceof CharSequence) { - text.setText((CharSequence)item); + text.setText((CharSequence) item); } else { text.setText(item.toString()); } @@ -426,7 +434,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * @see #getDropDownView(int, View, ViewGroup) */ @Override - public void setDropDownViewTheme(Resources.Theme theme) { + public void setDropDownViewTheme(@Nullable Resources.Theme theme) { if (theme == null) { mDropDownInflater = null; } else if (theme == mInflater.getContext().getTheme()) { @@ -438,12 +446,13 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp } @Override - public Resources.Theme getDropDownViewTheme() { + public @Nullable Resources.Theme getDropDownViewTheme() { return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme(); } @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { + public View getDropDownView(int position, @Nullable View convertView, + @NonNull ViewGroup parent) { final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater; return createViewFromResource(inflater, position, convertView, parent, mDropDownResource); } @@ -458,16 +467,14 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp * * @return An ArrayAdapter<CharSequence>. */ - public static ArrayAdapter<CharSequence> createFromResource(Context context, + public static @NonNull ArrayAdapter<CharSequence> createFromResource(@NonNull Context context, @ArrayRes int textArrayResId, @LayoutRes int textViewResId) { - CharSequence[] strings = context.getResources().getTextArray(textArrayResId); - return new ArrayAdapter<CharSequence>(context, textViewResId, strings); + final CharSequence[] strings = context.getResources().getTextArray(textArrayResId); + return new ArrayAdapter<>(context, textViewResId, strings); } - /** - * {@inheritDoc} - */ - public Filter getFilter() { + @Override + public @NonNull Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } @@ -482,31 +489,31 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp private class ArrayFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { - FilterResults results = new FilterResults(); + final FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { - mOriginalValues = new ArrayList<T>(mObjects); + mOriginalValues = new ArrayList<>(mObjects); } } if (prefix == null || prefix.length() == 0) { - ArrayList<T> list; + final ArrayList<T> list; synchronized (mLock) { - list = new ArrayList<T>(mOriginalValues); + list = new ArrayList<>(mOriginalValues); } results.values = list; results.count = list.size(); } else { - String prefixString = prefix.toString().toLowerCase(); + final String prefixString = prefix.toString().toLowerCase(); - ArrayList<T> values; + final ArrayList<T> values; synchronized (mLock) { - values = new ArrayList<T>(mOriginalValues); + values = new ArrayList<>(mOriginalValues); } final int count = values.size(); - final ArrayList<T> newValues = new ArrayList<T>(); + final ArrayList<T> newValues = new ArrayList<>(); for (int i = 0; i < count; i++) { final T value = values.get(i); @@ -517,11 +524,8 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp newValues.add(value); } else { final String[] words = valueText.split(" "); - final int wordCount = words.length; - - // Start at index 0, in case valueText starts with space(s) - for (int k = 0; k < wordCount; k++) { - if (words[k].startsWith(prefixString)) { + for (String word : words) { + if (word.startsWith(prefixString)) { newValues.add(value); break; } diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 6e3dbd8c5e1d..e3357a7738f2 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -75,8 +75,6 @@ import java.util.TimeZone; */ @Widget public class DatePicker extends FrameLayout { - private static final String LOG_TAG = DatePicker.class.getSimpleName(); - private static final int MODE_SPINNER = 1; private static final int MODE_CALENDAR = 2; diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 5adac0195701..332e89cc9653 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -378,9 +378,9 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mCurrentDate.set(Calendar.MONTH, monthOfYear); mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - mDateChangedListener = callBack; - onDateChanged(false, false); + + mDateChangedListener = callBack; } @Override diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java index 255de79dbf32..d8a3c5638b72 100644 --- a/core/java/android/widget/DatePickerSpinnerDelegate.java +++ b/core/java/android/widget/DatePickerSpinnerDelegate.java @@ -244,6 +244,7 @@ class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate { setDate(year, monthOfYear, dayOfMonth); updateSpinners(); updateCalendarView(); + mOnDateChangedListener = onDateChangedListener; } diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index fe8916bf0761..9ac49172ec89 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -16,17 +16,15 @@ package android.widget; -import java.util.ArrayList; +import com.android.internal.R; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StyleRes; import android.content.Context; -import android.content.res.ColorStateList; import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.Region; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; @@ -36,8 +34,7 @@ import android.view.ViewGroup; import android.view.ViewHierarchyEncoder; import android.widget.RemoteViews.RemoteView; -import com.android.internal.R; - +import java.util.ArrayList; /** * FrameLayout is designed to block out an area on the screen to display @@ -75,31 +72,29 @@ public class FrameLayout extends ViewGroup { @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingBottom = 0; - private final Rect mSelfBounds = new Rect(); - private final Rect mOverlayBounds = new Rect(); - - private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1); - - public FrameLayout(Context context) { + private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); + + public FrameLayout(@NonNull Context context) { super(context); } - - public FrameLayout(Context context, @Nullable AttributeSet attrs) { + + public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } - public FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public FrameLayout( - Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final TypedArray a = context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes); - - if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) { + attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes); + + if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) { setMeasureAllChildren(true); } @@ -171,10 +166,6 @@ public class FrameLayout extends ViewGroup { mPaddingBottom + mForegroundPaddingBottom; } - - /** - * {@inheritDoc} - */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); @@ -264,17 +255,13 @@ public class FrameLayout extends ViewGroup { } } } - - /** - * {@inheritDoc} - */ + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { layoutChildren(left, top, right, bottom, false /* no force left gravity */); } - void layoutChildren(int left, int top, int right, int bottom, - boolean forceLeftGravity) { + void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) { final int count = getChildCount(); final int parentLeft = getPaddingLeftWithForeground(); @@ -378,12 +365,9 @@ public class FrameLayout extends ViewGroup { return mMeasureAllChildren; } - /** - * {@inheritDoc} - */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { - return new FrameLayout.LayoutParams(getContext(), attrs); + return new FrameLayout.LayoutParams(getContext(), attrs); } @Override @@ -391,9 +375,6 @@ public class FrameLayout extends ViewGroup { return false; } - /** - * {@inheritDoc} - */ @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; @@ -431,34 +412,30 @@ public class FrameLayout extends ViewGroup { * Per-child layout information for layouts that support margins. * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} * for a list of all child view attributes that this class supports. - * + * * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity */ public static class LayoutParams extends MarginLayoutParams { /** * The gravity to apply with the View to which these layout parameters * are associated. + * <p> + * The default value is {@code Gravity.TOP | Gravity.START} * * @see android.view.Gravity - * * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity */ - public int gravity = -1; + public int gravity = DEFAULT_CHILD_GRAVITY; - /** - * {@inheritDoc} - */ - public LayoutParams(Context c, AttributeSet attrs) { + public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) { super(c, attrs); - TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout); - gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1); + final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout); + gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, + DEFAULT_CHILD_GRAVITY); a.recycle(); } - /** - * {@inheritDoc} - */ public LayoutParams(int width, int height) { super(width, height); } @@ -468,9 +445,9 @@ public class FrameLayout extends ViewGroup { * and weight. * * @param width the width, either {@link #MATCH_PARENT}, - * {@link #WRAP_CONTENT} or a fixed size in pixels + * {@link #WRAP_CONTENT} or a fixed size in pixels * @param height the height, either {@link #MATCH_PARENT}, - * {@link #WRAP_CONTENT} or a fixed size in pixels + * {@link #WRAP_CONTENT} or a fixed size in pixels * @param gravity the gravity * * @see android.view.Gravity @@ -480,17 +457,11 @@ public class FrameLayout extends ViewGroup { this.gravity = gravity; } - /** - * {@inheritDoc} - */ - public LayoutParams(ViewGroup.LayoutParams source) { + public LayoutParams(@NonNull ViewGroup.LayoutParams source) { super(source); } - /** - * {@inheritDoc} - */ - public LayoutParams(ViewGroup.MarginLayoutParams source) { + public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) { super(source); } @@ -500,7 +471,7 @@ public class FrameLayout extends ViewGroup { * * @param source The layout params to copy from. */ - public LayoutParams(LayoutParams source) { + public LayoutParams(@NonNull LayoutParams source) { super(source); this.gravity = source.gravity; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 02065779ef91..222a040d2b3c 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -911,17 +911,11 @@ public class ImageView extends View { } if (mDrawable != null) { - // It's possible for this method to be invoked from the constructor before - // subclass constructors have run. Drawables can and should trigger invalidations - // and other activity with their callback on visibility changes, which shouldn't - // happen before subclass constructors finish. However, we won't have set the - // drawable as visible until the view becomes attached. This guard below keeps - // multiple calls to this method from constructors from causing issues. - if (mDrawable.isVisible()) { - mDrawable.setVisible(false, false); - } mDrawable.setCallback(null); unscheduleDrawable(mDrawable); + if (isAttachedToWindow()) { + mDrawable.setVisible(false, false); + } } mDrawable = d; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 8bd63dfc2075..6d2cea6b5c3c 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.ColorInt; +import android.app.ActivityManager.StackId; import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application; @@ -228,6 +229,11 @@ public class RemoteViews implements Parcelable, Filter { public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { + return onClickHandler(view, pendingIntent, fillInIntent, StackId.INVALID_STACK_ID); + } + + public boolean onClickHandler(View view, PendingIntent pendingIntent, + Intent fillInIntent, int launchStackId) { try { // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? Context context = view.getContext(); @@ -239,6 +245,10 @@ public class RemoteViews implements Parcelable, Filter { 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); } + + if (launchStackId != StackId.INVALID_STACK_ID) { + opts.setLaunchStackId(launchStackId); + } context.startIntentSender( pendingIntent.getIntentSender(), fillInIntent, Intent.FLAG_ACTIVITY_NEW_TASK, diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 753c0691222e..b7ac6008a1fd 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -516,9 +516,15 @@ public class AlertController { } // Only show the divider if we have a title. - final View divider; + View divider = null; if (mMessage != null || mListView != null || hasCustomPanel) { - divider = topPanel.findViewById(R.id.titleDivider); + if (!hasCustomPanel) { + divider = topPanel.findViewById(R.id.titleDividerNoCustom); + } + if (divider == null) { + divider = topPanel.findViewById(R.id.titleDivider); + } + } else { divider = topPanel.findViewById(R.id.titleDividerTop); } @@ -526,6 +532,17 @@ public class AlertController { if (divider != null) { divider.setVisibility(View.VISIBLE); } + } else { + if (contentPanel != null) { + final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle); + if (spacer != null) { + spacer.setVisibility(View.VISIBLE); + } + } + } + + if (mListView instanceof RecycleListView) { + ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel); } // Update scroll indicators as needed. @@ -861,23 +878,34 @@ public class AlertController { } public static class RecycleListView extends ListView { + private final int mPaddingTopNoTitle; + private final int mPaddingBottomNoButtons; + boolean mRecycleOnMeasure = true; public RecycleListView(Context context) { - super(context); + this(context, null); } public RecycleListView(Context context, AttributeSet attrs) { super(context, attrs); - } - public RecycleListView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); + final TypedArray ta = context.obtainStyledAttributes( + attrs, R.styleable.RecycleListView); + mPaddingBottomNoButtons = ta.getDimensionPixelOffset( + R.styleable.RecycleListView_paddingBottomNoButtons, -1); + mPaddingTopNoTitle = ta.getDimensionPixelOffset( + R.styleable.RecycleListView_paddingTopNoTitle, -1); } - public RecycleListView( - Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); + public void setHasDecor(boolean hasTitle, boolean hasButtons) { + if (!hasButtons || !hasTitle) { + final int paddingLeft = getPaddingLeft(); + final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle; + final int paddingRight = getPaddingRight(); + final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons; + setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } } @Override diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java index 36db5d742b4a..a9d51132cdcc 100644 --- a/core/java/com/android/internal/app/LocaleHelper.java +++ b/core/java/com/android/internal/app/LocaleHelper.java @@ -219,7 +219,7 @@ public class LocaleHelper { public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) { // We don't care about the various suggestion types, just "suggested" (!= 0) // and "all others" (== 0) - if (mCountryMode || (lhs.isSuggested() == rhs.isSuggested())) { + if (lhs.isSuggested() == rhs.isSuggested()) { // They are in the same "bucket" (suggested / others), so we compare the text return mCollator.compare( removePrefixForCompare(lhs.getLocale(), lhs.getLabel(mCountryMode)), diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index c4e6675c2c92..7803e52c45e1 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -31,8 +31,9 @@ public class LocaleStore { private static boolean sFullyInitialized = false; public static class LocaleInfo { - private static final int SUGGESTION_TYPE_NONE = 0x00; - private static final int SUGGESTION_TYPE_SIM = 0x01; + private static final int SUGGESTION_TYPE_NONE = 0; + private static final int SUGGESTION_TYPE_SIM = 1 << 0; + private static final int SUGGESTION_TYPE_CFG = 1 << 1; private final Locale mLocale; private final Locale mParent; @@ -273,6 +274,22 @@ public class LocaleStore { final HashSet<String> localizedLocales = new HashSet<>(); for (String localeId : LocalePicker.getSystemAssetLocales()) { LocaleInfo li = new LocaleInfo(localeId); + final String country = li.getLocale().getCountry(); + // All this is to figure out if we should suggest a country + if (!country.isEmpty()) { + LocaleInfo cachedLocale = null; + if (sLocaleCache.containsKey(li.getId())) { // the simple case, e.g. fr-CH + cachedLocale = sLocaleCache.get(li.getId()); + } else { // e.g. zh-TW localized, zh-Hant-TW in cache + final String langScriptCtry = li.getLangScriptKey() + "-" + country; + if (sLocaleCache.containsKey(langScriptCtry)) { + cachedLocale = sLocaleCache.get(langScriptCtry); + } + } + if (cachedLocale != null) { + cachedLocale.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CFG; + } + } localizedLocales.add(li.getLangScriptKey()); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 0e02ed6f126d..ff680e2002ac 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -243,10 +243,6 @@ public class ResolverActivity extends Activity { return; } - // Prevent the Resolver window from becoming the top fullscreen window and thus from taking - // control of the system bars. - getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR); - final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel); if (rdl != null) { rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() { diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 98102ea8627c..e2d29e313be4 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -49,6 +49,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private static final int TYPE_HEADER_SUGGESTED = 0; private static final int TYPE_HEADER_ALL_OTHERS = 1; private static final int TYPE_LOCALE = 2; + private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6; private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions; private ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions; @@ -171,7 +172,15 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { } private boolean showHeaders() { - if (mCountryMode) { // never show suggestions in country mode + // We don't want to show suggestions for locales with very few regions + // (e.g. Romanian, with 2 regions) + // So we put a (somewhat) arbitrary limit. + // + // The initial idea was to make that limit dependent on the screen height. + // But that would mean rotating the screen could make the suggestions disappear, + // as the number of countries that fits on the screen would be different in portrait + // and landscape mode. + if (mCountryMode && mLocaleOptions.size() < MIN_REGIONS_FOR_SUGGESTIONS) { return false; } return mSuggestionCount != 0 && mSuggestionCount != mLocaleOptions.size(); diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index ee73b90097af..bed5a2e219ae 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -238,6 +238,14 @@ public class ArrayUtils { return total; } + public static int[] convertToIntArray(List<Integer> list) { + int[] array = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + array[i] = list.get(i); + } + return array; + } + /** * Adds value to given array if not already present, providing set-like * behavior. diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index bd0e6cef8ae9..d8be9fdc33bd 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -402,7 +402,7 @@ public class AsyncChannel { // Initialize destination fields mDstMessenger = dstMessenger; - + linkToDeathMonitor(); if (DBG) log("connected srcHandler to the dstMessenger X"); } @@ -844,22 +844,30 @@ public class AsyncChannel { msg.arg1 = status; msg.obj = this; msg.replyTo = mDstMessenger; + if (!linkToDeathMonitor()) { + // Override status to indicate failure + msg.arg1 = STATUS_BINDING_UNSUCCESSFUL; + } - /* - * Link to death only when bindService isn't used. - */ - if (mConnection == null) { + mSrcHandler.sendMessage(msg); + } + + /** + * Link to death monitor for destination messenger. Returns true if successfully binded to + * destination messenger; false otherwise. + */ + private boolean linkToDeathMonitor() { + // Link to death only when bindService isn't used and not already linked. + if (mConnection == null && mDeathMonitor == null) { mDeathMonitor = new DeathMonitor(); try { mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0); } catch (RemoteException e) { mDeathMonitor = null; - // Override status to indicate failure - msg.arg1 = STATUS_BINDING_UNSUCCESSFUL; + return false; } } - - mSrcHandler.sendMessage(msg); + return true; } /** diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 7c8524671716..1ead5b3de1de 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -191,6 +191,21 @@ public class Preconditions { * Ensures that that the argument numeric value is non-negative. * * @param value a numeric long value + * @return the validated numeric value + * @throws IllegalArgumentException if {@code value} was negative + */ + public static long checkArgumentNonnegative(final long value) { + if (value < 0) { + throw new IllegalArgumentException(); + } + + return value; + } + + /** + * Ensures that that the argument numeric value is non-negative. + * + * @param value a numeric long value * @param errorMessage the exception message to use if the check fails * @return the validated numeric value * @throws IllegalArgumentException if {@code value} was negative diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 9d1447836b93..3d892afbb5f3 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1099,7 +1099,8 @@ public class LockPatternUtils { || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; + || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX + || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED; return passwordEnabled && savedPasswordExists(userId); } diff --git a/services/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 30e0ceb52af3..07912cd43f07 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -19,6 +19,7 @@ package com.android.server; import static com.android.internal.util.ArrayUtils.appendInt; import android.app.ActivityManager; +import android.content.ComponentName; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import android.os.Environment; @@ -115,6 +116,9 @@ public class SystemConfig { // These are the packages that should not run under system user final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>(); + // These are the components that are enabled by default as VR mode listener services. + final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>(); + public static SystemConfig getInstance() { synchronized (SystemConfig.class) { if (sInstance == null) { @@ -168,6 +172,10 @@ public class SystemConfig { return mSystemUserBlacklistedApps; } + public ArraySet<ComponentName> getDefaultVrComponents() { + return mDefaultVrComponents; + } + SystemConfig() { // Read configuration from system readPermissions(Environment.buildPath( @@ -431,6 +439,19 @@ public class SystemConfig { mSystemUserBlacklistedApps.add(pkgname); } XmlUtils.skipCurrentTag(parser); + } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) { + String pkgname = parser.getAttributeValue(null, "package"); + String clsname = parser.getAttributeValue(null, "class"); + if (pkgname == null) { + Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile + + " at " + parser.getPositionDescription()); + } else if (clsname == null) { + Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mDefaultVrComponents.add(new ComponentName(pkgname, clsname)); + } + XmlUtils.skipCurrentTag(parser); } else { XmlUtils.skipCurrentTag(parser); continue; diff --git a/core/java/com/android/server/backup/ShortcutBackupHelper.java b/core/java/com/android/server/backup/ShortcutBackupHelper.java new file mode 100644 index 000000000000..0b3f2aeac35a --- /dev/null +++ b/core/java/com/android/server/backup/ShortcutBackupHelper.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.backup; + +import android.app.backup.BlobBackupHelper; +import android.content.Context; +import android.content.pm.IShortcutService; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Slog; + +public class ShortcutBackupHelper extends BlobBackupHelper { + private static final String TAG = "ShortcutBackupAgent"; + private static final int BLOB_VERSION = 1; + + private static final String KEY_USER_FILE = "shortcutuser.xml"; + + public ShortcutBackupHelper() { + super(BLOB_VERSION, KEY_USER_FILE); + } + + private IShortcutService getShortcutService() { + return IShortcutService.Stub.asInterface( + ServiceManager.getService(Context.SHORTCUT_SERVICE)); + } + + @Override + protected byte[] getBackupPayload(String key) { + switch (key) { + case KEY_USER_FILE: + try { + return getShortcutService().getBackupPayload(UserHandle.USER_SYSTEM); + } catch (Exception e) { + Slog.wtf(TAG, "Backup failed", e); + } + break; + default: + Slog.w(TAG, "Unknown key: " + key); + } + return null; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + switch (key) { + case KEY_USER_FILE: + try { + getShortcutService().applyRestore(payload, UserHandle.USER_SYSTEM); + } catch (Exception e) { + Slog.wtf(TAG, "Restore failed", e); + } + break; + default: + Slog.w(TAG, "Unknown key: " + key); + } + } +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 181ed5144310..2d12fcda87c1 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -17,9 +17,9 @@ package com.android.server.backup; import android.app.IWallpaperManager; +import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; -import android.app.backup.BackupAgentHelper; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import android.app.backup.WallpaperBackupHelper; @@ -48,6 +48,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String NOTIFICATION_HELPER = "notifications"; private static final String PERMISSION_HELPER = "permissions"; private static final String USAGE_STATS_HELPER = "usage_stats"; + private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are @@ -100,6 +101,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); + addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); super.onBackup(oldState, data, newState); } @@ -138,6 +140,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); + addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); try { super.onRestore(data, appVersionCode, newState); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 27b98306b72b..22a81d401df8 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -251,13 +251,6 @@ SkPixelRef* Bitmap::refPixelRefLocked() { void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) { - { - android::AutoMutex _lock(mLock); - if (mPinnedRefCount) { - ALOGW("Called reconfigure on a bitmap that is in use! This may" - " cause graphical corruption!"); - } - } mPixelRef->reconfigure(info, rowBytes, ctable); } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 27d8fed51e8a..1844a9870ada 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -823,7 +823,7 @@ namespace PaintGlue { static jfloat doRunAdvance(const Paint* paint, Typeface* typeface, const jchar buf[], jint start, jint count, jint bufSize, jboolean isRtl, jint offset) { int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; - if (offset == count) { + if (offset == start + count) { return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, nullptr); } diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp index 87247292a73b..42dc983792d2 100644 --- a/core/jni/android_hardware_location_ContextHubService.cpp +++ b/core/jni/android_hardware_location_ContextHubService.cpp @@ -16,23 +16,39 @@ #include "context_hub.h" +#define LOG_NDEBUG 0 +#define LOG_TAG "ContextHubService" + +#include <inttypes.h> +#include <jni.h> +#include <map> +#include <queue> #include <string.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> + +#include <cutils/log.h> -#include <jni.h> #include "JNIHelp.h" #include "core_jni_helpers.h" -#include "stdint.h" -#include "stdlib.h" + +//static constexpr int OS_APP_ID=-1; + +static constexpr int MIN_APP_ID=1; +static constexpr int MAX_APP_ID=128; + +static constexpr size_t MSG_HEADER_SIZE=4; +static constexpr int HEADER_FIELD_MSG_TYPE=0; +//static constexpr int HEADER_FIELD_MSG_VERSION=1; +static constexpr int HEADER_FIELD_HUB_HANDLE=2; +static constexpr int HEADER_FIELD_APP_INSTANCE=3; namespace android { namespace { -// TODO: We should share this array_length function widely around Android -// code. /* * Finds the length of a statically-sized array using template trickery that * also prevents it from being applied to the wrong type. @@ -64,35 +80,207 @@ struct jniInfo_s { jmethodID contextHubInfoSetPeakPowerDrawMw; jmethodID contextHubInfoSetSupportedSensors; jmethodID contextHubInfoSetMemoryRegions; + jmethodID contextHubInfoSetMaxPacketLenBytes; jmethodID contextHubServiceMsgReceiptCallback; + jmethodID contextHubServiceAddAppInstance; }; struct context_hub_info_s { - int cookie; + uint32_t *cookies; int numHubs; const struct context_hub_t *hubs; struct context_hub_module_t *contextHubModule; }; +struct app_instance_info_s { + uint32_t hubHandle; // Id of the hub this app is on + int instanceId; // systemwide unique instance id - assigned + struct hub_app_info appInfo; // returned from the HAL + uint64_t truncName; // Possibly truncated name - logging +}; + struct contextHubServiceDb_s { int initialized; context_hub_info_s hubInfo; jniInfo_s jniInfo; + std::queue<int> freeIds; + std::map<int, app_instance_info_s *> appInstances; }; } // unnamed namespace static contextHubServiceDb_s db; -int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg, +int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg, void *cookie); +const context_hub_t *get_hub_info(int hubHandle) { + if (hubHandle >= 0 && hubHandle < db.hubInfo.numHubs) { + return &db.hubInfo.hubs[hubHandle]; + } + return nullptr; +} + +static int send_msg_to_hub(const hub_message_t *msg, int hubHandle) { + const context_hub_t *info = get_hub_info(hubHandle); + + if (info) { + return db.hubInfo.contextHubModule->send_message(info->hub_id, msg); + } else { + ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle); + return -1; + } +} + +static int set_os_app_as_destination(hub_message_t *msg, int hubHandle) { + const context_hub_t *info = get_hub_info(hubHandle); + + if (info) { + msg->app = info->os_app_name; + return 0; + } else { + ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle); + return -1; + } +} + +static int get_hub_id_for_app_instance(int id) { + if (db.appInstances.find(id) == db.appInstances.end()) { + ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id); + return -1; + } + + int hubHandle = db.appInstances[id]->hubHandle; + + return db.hubInfo.hubs[hubHandle].hub_id; +} + +static int set_dest_app(hub_message_t *msg, int id) { + if (db.appInstances.find(id) == db.appInstances.end()) { + ALOGD("%s: Cannod find app for app instance %d", __FUNCTION__, id); + return -1; + } + + msg->app = db.appInstances[id]->appInfo.name; + return 0; +} + +static void send_query_for_apps() { + hub_message_t msg; + + msg.message_type = CONTEXT_HUB_QUERY_APPS; + msg.message_len = 0; + + for (int i = 0; i < db.hubInfo.numHubs; i++ ) { + ALOGD("Sending query for apps to hub %d", i); + set_os_app_as_destination(&msg, i); + if (send_msg_to_hub(&msg, i) != 0) { + ALOGW("Could not query hub %i for apps", i); + } + } +} + +static int return_id(int id) { + // Note : This method is not thread safe. + // id returned is guarenteed to be in use + db.freeIds.push(id); + return 0; +} + +static int generate_id(void) { + // Note : This method is not thread safe. + int retVal = -1; + + if (!db.freeIds.empty()) { + retVal = db.freeIds.front(); + db.freeIds.pop(); + } + + return retVal; +} + +int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) { + // Not checking if the apps are indeed distinct + + app_instance_info_s *entry; + void *appName; + hub_app_name_t *name; + + assert(appInfo && appInfo->name && appInfo->name->app_name); + + entry = (app_instance_info_s *) malloc(sizeof(app_instance_info_s)); + appName = malloc(appInfo->name->app_name_len); + name = (hub_app_name_t *) malloc(sizeof(hub_app_name_t)); + + int appInstanceHandle = generate_id(); + + if (appInstanceHandle < 0 || !appName || !entry || !name) { + ALOGE("Cannot find resources to add app instance %d, %p, %p", + appInstanceHandle, appName, entry); + + free(appName); + free(entry); + free(name); + + if (appInstanceHandle >= 0) { + return_id(appInstanceHandle); + } + + return -1; + } + + memcpy(&(entry->appInfo), appInfo, sizeof(entry->appInfo)); + memcpy(appName, appInfo->name->app_name, appInfo->name->app_name_len); + name->app_name = appName; + name->app_name_len = appInfo->name->app_name_len; + entry->appInfo.name = name; + entry->truncName = 0; + memcpy(&(entry->truncName), name->app_name, + sizeof(entry->truncName) < name->app_name_len ? + sizeof(entry->truncName) : name->app_name_len); + + // Not checking for sanity of hubId + entry->hubHandle = hubHandle; + entry->instanceId = appInstanceHandle; + db.appInstances[appInstanceHandle] = entry; + + // Finally - let the service know of this app instance + env->CallIntMethod(db.jniInfo.jContextHubService, + db.jniInfo.contextHubServiceAddAppInstance, + hubHandle, entry->instanceId, entry->truncName, + entry->appInfo.version); + + ALOGW("Added App 0x%" PRIx64 " on hub Handle %" PRId32 + " as appInstance %d, original name_length %" PRId32, entry->truncName, + entry->hubHandle, appInstanceHandle, name->app_name_len); + + return appInstanceHandle; +} + +int delete_app_instance(int id) { + if (db.appInstances.find(id) == db.appInstances.end()) { + return -1; + } + + return_id(id); + + if (db.appInstances[id]) { + // Losing the const cast below. This is intentional. + free((void *)db.appInstances[id]->appInfo.name->app_name); + free((void *)db.appInstances[id]->appInfo.name); + free(db.appInstances[id]); + db.appInstances.erase(id); + } + + return 0; +} + + static void initContextHubService() { int err = 0; - db.hubInfo.hubs = NULL; + db.hubInfo.hubs = nullptr; db.hubInfo.numHubs = 0; - db.hubInfo.cookie = 0; int i; err = hw_get_module(CONTEXT_HUB_MODULE_ID, @@ -103,26 +291,45 @@ static void initContextHubService() { strerror(-err)); } + // Prep for storing app info + for(i = MIN_APP_ID; i <= MAX_APP_ID; i++) { + db.freeIds.push(i); + } + if (db.hubInfo.contextHubModule) { - ALOGD("Fetching hub info"); - db.hubInfo.numHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule, + int retNumHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule, &db.hubInfo.hubs); - - if (db.hubInfo.numHubs > 0) { - for (i = 0; i < db.hubInfo.numHubs; i++) { - // TODO : Event though one cookie is OK for now, lets change - // this to be one per hub - db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id, - context_hub_callback, - &db.hubInfo.cookie); + ALOGD("ContextHubModule returned %d hubs ", retNumHubs); + db.hubInfo.numHubs = retNumHubs; + + if (db.hubInfo.numHubs > 0) { + db.hubInfo.numHubs = retNumHubs; + db.hubInfo.cookies = (uint32_t *)malloc(sizeof(uint32_t) * db.hubInfo.numHubs); + + if (!db.hubInfo.cookies) { + ALOGW("Ran out of memory allocating cookies, bailing"); + return; + } + + for (i = 0; i < db.hubInfo.numHubs; i++) { + db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id; + if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id, + context_hub_callback, + &db.hubInfo.cookies[i]) == 0) { + } + } } - } + + send_query_for_apps(); + } else { + ALOGW("No Context Hub Module present"); } } static int onMessageReceipt(int *header, int headerLen, char *msg, int msgLen) { JNIEnv *env; - if ((db.jniInfo.vm)->AttachCurrentThread(&env, NULL) != JNI_OK) { + + if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { return -1; } @@ -132,28 +339,131 @@ static int onMessageReceipt(int *header, int headerLen, char *msg, int msgLen) { env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg); env->SetIntArrayRegion(jheader, 0, headerLen, (jint *)header); - - return env->CallIntMethod(db.jniInfo.jContextHubService, + return (env->CallIntMethod(db.jniInfo.jContextHubService, db.jniInfo.contextHubServiceMsgReceiptCallback, - jheader, jmsg); + jheader, jmsg) != 0); +} + +int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) { + int i; + JNIEnv *env; + if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { + return -1; + } + + int numApps = msgLen/sizeof(hub_app_info); + hub_app_info *info = (hub_app_info *)malloc(msgLen); // handle possible alignment + + if (!info) { + return -1; + } + + memcpy(info, msg, msgLen); + for (i = 0; i < numApps; i++) { + add_app_instance(info, hubHandle, env); + info++; + } + + free(info); + + return 0; +} + + +int handle_os_message(uint32_t msgType, uint32_t hubHandle, + char *msg, int msgLen) { + int retVal; + + switch(msgType) { + case CONTEXT_HUB_APPS_ENABLE: + retVal = 0; + break; + + case CONTEXT_HUB_APPS_DISABLE: + retVal = 0; + break; + + case CONTEXT_HUB_LOAD_APP: + retVal = 0; + break; + + case CONTEXT_HUB_UNLOAD_APP: + retVal = 0; + break; + + case CONTEXT_HUB_QUERY_APPS: + retVal = handle_query_apps_response(msg, msgLen, hubHandle); + break; + + case CONTEXT_HUB_QUERY_MEMORY: + retVal = 0; + break; + + case CONTEXT_HUB_LOAD_OS: + retVal = 0; + break; + + default: + retVal = -1; + break; + + } + + return retVal; +} + +static bool sanity_check_cookie(void *cookie, uint32_t hub_id) { + int *ptr = (int *)cookie; + + if (!ptr || *ptr >= db.hubInfo.numHubs) { + return false; + } + + if (db.hubInfo.hubs[*ptr].hub_id != hub_id) { + return false; + } else { + return true; + } } -int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg, +int context_hub_callback(uint32_t hubId, + const struct hub_message_t *msg, void *cookie) { - int msgHeader[4]; + int msgHeader[MSG_HEADER_SIZE]; - msgHeader[0] = msg->message_type; - msgHeader[1] = 0; // TODO : HAL does not have a version field - msgHeader[2] = hub_id; + if (!msg) { + return -1; + } + + msgHeader[HEADER_FIELD_MSG_TYPE] = msg->message_type; + + if (!sanity_check_cookie(cookie, hubId)) { + ALOGW("Incorrect cookie %" PRId32 " for cookie %p! Bailing", + hubId, cookie); + + return -1; + } + + msgHeader[HEADER_FIELD_HUB_HANDLE] = *(uint32_t*)cookie; + + if (msgHeader[HEADER_FIELD_MSG_TYPE] < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE && + msgHeader[HEADER_FIELD_MSG_TYPE] != 0 ) { + handle_os_message(msgHeader[HEADER_FIELD_MSG_TYPE], + msgHeader[HEADER_FIELD_HUB_HANDLE], + (char *)msg->message, + msg->message_len); + } else { + onMessageReceipt(msgHeader, sizeof(msgHeader), + (char *)msg->message, msg->message_len); + } - onMessageReceipt(msgHeader, sizeof(msgHeader), (char *)msg->message, msg->message_len); // TODO : Populate this - return 0; + return 0; } static int init_jni(JNIEnv *env, jobject instance) { if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) { - return -1; + return -1; } db.jniInfo.jContextHubService = env->NewGlobalRef(instance); @@ -167,7 +477,6 @@ static int init_jni(JNIEnv *env, jobject instance) { db.jniInfo.memoryRegionsClass = env->FindClass("android/hardware/location/MemoryRegion"); - //TODO :: Add error checking db.jniInfo.contextHubInfoCtor = env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V"); db.jniInfo.contextHubInfoSetId = @@ -209,6 +518,9 @@ static int init_jni(JNIEnv *env, jobject instance) { db.jniInfo.contextHubInfoSetMemoryRegions = env->GetMethodID(db.jniInfo.contextHubInfoClass, "setMemoryRegions", "([Landroid/hardware/location/MemoryRegion;)V"); + db.jniInfo.contextHubInfoSetMaxPacketLenBytes = + env->GetMethodID(db.jniInfo.contextHubInfoClass, + "setMaxPacketLenBytes", "(I)V"); db.jniInfo.contextHubServiceMsgReceiptCallback = @@ -218,6 +530,11 @@ static int init_jni(JNIEnv *env, jobject instance) { env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", "(Ljava/lang/String;)V"); + db.jniInfo.contextHubServiceAddAppInstance = + env->GetMethodID(db.jniInfo.contextHubServiceClass, + "addAppInstance", "(IIJI)I"); + + return 0; } @@ -245,20 +562,29 @@ static jobject constructJContextHubInfo(JNIEnv *env, const struct context_hub_t env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub->platform_version); env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub->toolchain_version); env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub->peak_mips); - env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, hub->stopped_power_draw_mw); - env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, hub->sleep_power_draw_mw); - env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, hub->peak_power_draw_mw); + env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, + hub->stopped_power_draw_mw); + env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, + hub->sleep_power_draw_mw); + env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, + hub->peak_power_draw_mw); + env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes, + hub->max_supported_msg_len); + // TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors); - // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, hub->connected_sensors); + // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, + // hub->connected_sensors); jintBuf = env->NewIntArray(array_length(dummyConnectedSensors)); env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors); + env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf); // We are not getting the memory regions from the CH Hal - change this when it is available - jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, NULL); + jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr); // Note the zero size above. We do not need to set any elements env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf); + return jHub; } @@ -267,18 +593,18 @@ static jobjectArray nativeInitialize(JNIEnv *env, jobject instance) jobject hub; jobjectArray retArray; - initContextHubService(); - if (init_jni(env, instance) < 0) { - return NULL; + return nullptr; } - // Note : The service is clamping the number of hubs to 1 - db.hubInfo.numHubs = 1; - initContextHubService(); - retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, NULL); + if (db.hubInfo.numHubs > 1) { + ALOGW("Clamping the number of hubs to 1"); + db.hubInfo.numHubs = 1; + } + + retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr); for(int i = 0; i < db.hubInfo.numHubs; i++) { hub = constructJContextHubInfo(env, &db.hubInfo.hubs[i]); @@ -291,28 +617,27 @@ static jobjectArray nativeInitialize(JNIEnv *env, jobject instance) static jint nativeSendMessage(JNIEnv *env, jobject instance, jintArray header_, jbyteArray data_) { hub_message_t msg; - hub_app_name_t dest; - uint8_t os_name[8]; - - memset(os_name, 0, sizeof(os_name)); + jint retVal = -1; // Default to failure jint *header = env->GetIntArrayElements(header_, 0); - //int numHeaderElements = env->GetArrayLength(header_); + unsigned int numHeaderElements = env->GetArrayLength(header_); jbyte *data = env->GetByteArrayElements(data_, 0); int dataBufferLength = env->GetArrayLength(data_); - /* Assume an int - thats all we understand */ - dest.app_name_len = array_length(os_name); // TODO : Check this - //dest.app_name = &header[1]; - dest.app_name = os_name; - - msg.app = &dest; - - msg.message_type = header[3]; - msg.message_len = dataBufferLength; - msg.message = data; - - jint retVal = db.hubInfo.contextHubModule->send_message(header[0], &msg); + if (numHeaderElements >= MSG_HEADER_SIZE) { + if (set_dest_app(&msg, header[HEADER_FIELD_APP_INSTANCE]) == 0) { + msg.message_type = header[HEADER_FIELD_MSG_TYPE]; + msg.message_len = dataBufferLength; + msg.message = data; + retVal = db.hubInfo.contextHubModule->send_message( + get_hub_id_for_app_instance(header[HEADER_FIELD_APP_INSTANCE]), + &msg); + } else { + ALOGD("Could not find app instance %d", header[HEADER_FIELD_APP_INSTANCE]); + } + } else { + ALOGD("Malformed header len"); + } env->ReleaseIntArrayElements(header_, header, 0); env->ReleaseByteArrayElements(data_, data, 0); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b0fcc2897445..30a59934e0a4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -845,14 +845,14 @@ --> <permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE" android:permissionGroup="android.permission-group.PHONE" - android:protectionLevel="dangerous"/> + android:protectionLevel="signatureOrSystem"/> <!-- @hide Allows an application to Access UCE-OPTIONS. <p>Protection level: dangerous --> <permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE" android:permissionGroup="android.permission-group.PHONE" - android:protectionLevel="dangerous"/> + android:protectionLevel="signatureOrSystem"/> @@ -2004,6 +2004,11 @@ <permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows but does not guarantee access to user passwords at the conclusion of add + account --> + <permission android:name="android.permission.GET_PASSWORD_PRIVILEGED" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows applications to RW to diagnostic resources. <p>Not for use by third-party applications. --> <permission android:name="android.permission.DIAGNOSTIC" @@ -2984,6 +2989,11 @@ <permission android:name="android.permission.BIND_VR_LISTENER_SERVICE" android:protectionLevel="signature" /> + <!-- Allows an application to whitelist tasks during lock task mode + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES" + android:protectionLevel="signature|setup" /> + <application android:process="system" android:persistent="true" android:hasCode="false" @@ -2993,7 +3003,7 @@ android:killAfterRestore="false" android:icon="@drawable/ic_launcher_android" android:supportsRtl="true" - android:theme="@style/Theme.Material.DayNight.DarkActionBar" + android:theme="@style/Theme.Material.Light.DarkActionBar" android:defaultToDeviceProtectedStorage="true" android:directBootAware="true"> <activity android:name="com.android.internal.app.ChooserActivity" @@ -3029,7 +3039,7 @@ android:label="@string/managed_profile_label"> </activity-alias> <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity" - android:theme="@style/Theme.Material.DayNight.Dialog" + android:theme="@style/Theme.Material.Light.Dialog" android:label="@string/heavy_weight_switcher_title" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" @@ -3062,7 +3072,7 @@ <activity android:name="android.accounts.ChooseAccountActivity" android:excludeFromRecents="true" android:exported="true" - android:theme="@style/Theme.Material.DayNight.Dialog" + android:theme="@style/Theme.Material.Light.Dialog" android:label="@string/choose_account_label" android:process=":ui"> </activity> @@ -3070,14 +3080,14 @@ <activity android:name="android.accounts.ChooseTypeAndAccountActivity" android:excludeFromRecents="true" android:exported="true" - android:theme="@style/Theme.Material.DayNight.Dialog" + android:theme="@style/Theme.Material.Light.Dialog" android:label="@string/choose_account_label" android:process=":ui"> </activity> <activity android:name="android.accounts.ChooseAccountTypeActivity" android:excludeFromRecents="true" - android:theme="@style/Theme.Material.DayNight.Dialog" + android:theme="@style/Theme.Material.Light.Dialog" android:label="@string/choose_account_label" android:process=":ui"> </activity> @@ -3085,19 +3095,19 @@ <activity android:name="android.accounts.CantAddAccountActivity" android:excludeFromRecents="true" android:exported="true" - android:theme="@style/Theme.Material.DayNight.Dialog.NoActionBar" + android:theme="@style/Theme.Material.Light.Dialog.NoActionBar" android:process=":ui"> </activity> <activity android:name="android.accounts.GrantCredentialsPermissionActivity" android:excludeFromRecents="true" android:exported="true" - android:theme="@style/Theme.Material.DayNight.DialogWhenLarge" + android:theme="@style/Theme.Material.Light.DialogWhenLarge" android:process=":ui"> </activity> <activity android:name="android.content.SyncActivityTooManyDeletes" - android:theme="@style/Theme.Material.DayNight.Dialog" + android:theme="@style/Theme.Material.Light.Dialog" android:label="@string/sync_too_many_deletes" android:process=":ui"> </activity> @@ -3117,7 +3127,7 @@ </activity> <activity android:name="com.android.internal.app.NetInitiatedActivity" - android:theme="@style/Theme.Material.DayNight.Dialog.Alert" + android:theme="@style/Theme.Material.Light.Dialog.Alert" android:excludeFromRecents="true" android:process=":ui"> </activity> @@ -3138,7 +3148,7 @@ <activity android:name="com.android.internal.app.ConfirmUserCreationActivity" android:excludeFromRecents="true" android:process=":ui" - android:theme="@style/Theme.Material.DayNight.Dialog.Alert"> + android:theme="@style/Theme.Material.Light.Dialog.Alert"> <intent-filter android:priority="1000"> <action android:name="android.os.action.CREATE_USER" /> <category android:name="android.intent.category.DEFAULT" /> @@ -3146,7 +3156,7 @@ </activity> <activity android:name="com.android.internal.app.UnlaunchableAppActivity" - android:theme="@style/Theme.Material.DayNight.Dialog.Alert" + android:theme="@style/Theme.Material.Light.Dialog.Alert" android:excludeFromRecents="true" android:process=":ui"> </activity> diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml index 9f5093799cd9..6d33de6ca307 100644 --- a/core/res/res/layout/alert_dialog_material.xml +++ b/core/res/res/layout/alert_dialog_material.xml @@ -34,7 +34,6 @@ android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/dialog_padding_top_material" android:clipToPadding="false"> <LinearLayout @@ -42,6 +41,12 @@ android:layout_height="wrap_content" android:orientation="vertical"> + <Space + android:id="@+id/textSpacerNoTitle" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@dimen/dialog_padding_top_material" /> + <TextView android:id="@+id/message" android:layout_width="match_parent" @@ -53,7 +58,7 @@ <Space android:id="@+id/textSpacerNoButtons" android:visibility="gone" - android:layout_width="0dp" + android:layout_width="match_parent" android:layout_height="@dimen/dialog_padding_top_material" /> </LinearLayout> </ScrollView> diff --git a/core/res/res/layout/alert_dialog_title_material.xml b/core/res/res/layout/alert_dialog_title_material.xml index 738a6373b338..eef95859cab6 100644 --- a/core/res/res/layout/alert_dialog_title_material.xml +++ b/core/res/res/layout/alert_dialog_title_material.xml @@ -21,6 +21,8 @@ android:layout_height="wrap_content" android:orientation="vertical"> + <!-- If the client uses a customTitle, it will be added here. --> + <LinearLayout android:id="@+id/title_template" android:layout_width="match_parent" @@ -49,5 +51,9 @@ style="?attr/windowTitleStyle" /> </LinearLayout> - <!-- If the client uses a customTitle, it will be added here. --> + <Space + android:id="@+id/titleDividerNoCustom" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@dimen/dialog_title_divider_material" /> </LinearLayout> diff --git a/core/res/res/layout/app_anr_dialog.xml b/core/res/res/layout/app_anr_dialog.xml index 8bef116fa89e..5ad0f4c0f6cc 100644 --- a/core/res/res/layout/app_anr_dialog.xml +++ b/core/res/res/layout/app_anr_dialog.xml @@ -19,7 +19,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="@dimen/aerr_padding_list_top" - android:paddingBottom="@dimen/dialog_list_padding_vertical_material"> + android:paddingBottom="@dimen/aerr_padding_list_bottom"> <Button android:id="@+id/aerr_close" diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml index b9531f46e275..7147ea24ee5d 100644 --- a/core/res/res/layout/app_error_dialog.xml +++ b/core/res/res/layout/app_error_dialog.xml @@ -22,7 +22,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="@dimen/aerr_padding_list_top" - android:paddingBottom="@dimen/dialog_list_padding_vertical_material"> + android:paddingBottom="@dimen/aerr_padding_list_bottom"> <Button diff --git a/core/res/res/layout/select_dialog_material.xml b/core/res/res/layout/select_dialog_material.xml index 19ad407d7a96..9a068f9adaec 100644 --- a/core/res/res/layout/select_dialog_material.xml +++ b/core/res/res/layout/select_dialog_material.xml @@ -30,6 +30,6 @@ android:scrollbars="vertical" android:overScrollMode="ifContentScrolls" android:textAlignment="viewStart" - android:paddingTop="@dimen/dialog_list_padding_vertical_material" - android:paddingBottom="@dimen/dialog_list_padding_vertical_material" - android:clipToPadding="false" /> + android:clipToPadding="false" + android:paddingBottomNoButtons="@dimen/dialog_list_padding_bottom_no_buttons" + android:paddingTopNoTitle="@dimen/dialog_list_padding_top_no_title" /> diff --git a/core/res/res/values-night/themes_material_daynight.xml b/core/res/res/values-night/themes_material_daynight.xml deleted file mode 100644 index b34458295ba3..000000000000 --- a/core/res/res/values-night/themes_material_daynight.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 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. ---> - -<!-- -=============================================================== - PLEASE READ -=============================================================== - -The Material themes must not be modified in order to pass CTS. -Many related themes and styles depend on other values defined in this file. -If you would like to provide custom themes and styles for your device, -please see themes_device_defaults.xml. - -=============================================================== - PLEASE READ -=============================================================== - --> -<resources> - - <!-- Material theme (day/night version) for activities. --> - <style name="Theme.Material.DayNight" parent="Theme.Material" /> - - <!-- Variant of Material.DayNight that has a solid (opaque) action bar - with an inverse color profile. The dark action bar sharply stands out against - the light content (when applicable). --> - <style name="Theme.Material.DayNight.DarkActionBar" parent="Theme.Material" /> - - <!-- Variant of Material.DayNight with no action bar. --> - <style name="Theme.Material.DayNight.NoActionBar" parent="Theme.Material.NoActionBar" /> - - <!-- Variant of Material.DayNight that has no title bar and fills - the entire screen. This theme - sets {@link android.R.attr#windowFullscreen} to true. --> - <style name="Theme.Material.DayNight.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen" /> - - <!-- Variant of Material.DayNight that has no title bar and fills - the entire screen and extends into the display overscan region. This theme - sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan} - to true. --> - <style name="Theme.Material.DayNight.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan" /> - - <!-- Variant of Material.DayNight that has no title bar and translucent - system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and - {@link android.R.attr#windowTranslucentNavigation} to true. --> - <style name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor" /> - - <!-- Default Material.DayNight theme for panel windows. This removes all extraneous - window decorations, so you basically have an empty rectangle in which - to place your content. It makes the window floating, with a transparent - background, and turns off dimming behind the window. --> - <style name="Theme.Material.DayNight.Panel" parent="Theme.Material.Panel" /> - - <!-- Material theme (day/night version) for dialog windows and activities, - which is used by the {@link android.app.Dialog} class. This changes - the window to be floating (not fill the entire screen), and puts a - frame around its contents. You can set this theme on an activity if - you would like to make an activity that looks like a Dialog. --> - <style name="Theme.Material.DayNight.Dialog" parent="Theme.Material.DayNight.BaseDialog" /> - <style name="Theme.Material.DayNight.BaseDialog" parent="Theme.Material.BaseDialog" /> - - <!-- Variant of Theme.Material.DayNight.Dialog that has a nice minimum width for - a regular dialog. --> - <style name="Theme.Material.DayNight.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth" /> - - <!-- Variant of Theme.Material.DayNight.Dialog that does not include a title bar. --> - <style name="Theme.Material.DayNight.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar" /> - - <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a nice minimum width for - a regular dialog. --> - <style name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth" /> - - <!-- Variant of Theme.Material.DayNight.Dialog that has a fixed size. --> - <style name="Theme.Material.DayNight.Dialog.FixedSize" parent="Theme.Material.Dialog.FixedSize" /> - - <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a fixed size. --> - <style name="Theme.Material.DayNight.Dialog.NoActionBar.FixedSize" parent="Theme.Material.Dialog.NoActionBar.FixedSize" /> - - <!-- Theme for a window that will be displayed either full-screen on - smaller screens (small, normal) or as a dialog on larger screens - (large, xlarge). --> - <style name="Theme.Material.DayNight.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge" /> - - <!-- Theme for a window with a dark action bar that will be displayed - either full-screen on smaller screens (small, normal) or as a dialog - on larger screens (large, xlarge). --> - <style name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" parent="Theme.Material.DialogWhenLarge" /> - - <!-- Theme for a window without an action bar that will be displayed either full-screen - on smaller screens (small, normal) or as a dialog on larger screens - (large, xlarge). --> - <style name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar" /> - - <!-- Theme for a presentation window on a secondary display. --> - <style name="Theme.Material.DayNight.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation" /> - - <!-- Material user theme for alert dialog windows, which is used by the - {@link android.app.AlertDialog} class. --> - <style name="Theme.Material.DayNight.Dialog.Alert" parent="Theme.Material.DayNight.Dialog.BaseAlert" /> - <style name="Theme.Material.DayNight.Dialog.BaseAlert" parent="Theme.Material.Dialog.BaseAlert" /> - - <style name="Theme.Material.DayNight.SearchBar" parent="Theme.Material.SearchBar" /> - <style name="Theme.Material.DayNight.CompactMenu" parent="Theme.Material.CompactMenu" /> - -</resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 44290010b746..0ed1f133c627 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3528,6 +3528,13 @@ i This setting implies fastScrollEnabled. --> <attr name="fastScrollAlwaysVisible" format="boolean" /> </declare-styleable> + <!-- @hide --> + <declare-styleable name="RecycleListView"> + <!-- Bottom padding to use when no buttons are present. --> + <attr name="paddingBottomNoButtons" format="dimension" /> + <!-- Top padding to use when no title is present. --> + <attr name="paddingTopNoTitle" format="dimension" /> + </declare-styleable> <declare-styleable name="AbsSpinner"> <!-- Reference to an array resource that will populate the Spinner. For static content, this is simpler than populating the Spinner programmatically. --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d1c08958f934..5b4364d45c73 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -225,6 +225,9 @@ granted any application pre-installed on the system image (not just privileged apps). --> <flag name="preinstalled" value="0x400" /> + <!-- Additional flag from base permission type: this permission can be automatically + granted to the setup wizard app --> + <flag name="setup" value="0x800" /> </attr> <!-- Flags indicating more context for a permission group. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 892b3d58b388..dbe3dcaa6344 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -578,6 +578,9 @@ Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> + <!-- Fast brightness animation ramp rate --> + <integer translatable="false" name="config_brightness_ramp_rate_fast">200</integer> + <!-- Don't name config resources like this. It should look like config_annoyDianne --> <bool name="config_annoy_dianne">true</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 71d9a1f6c6ef..a21f276873e8 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -450,6 +450,7 @@ <item type="dimen" format="integer" name="time_picker_column_end_material">1</item> <item type="dimen" name="aerr_padding_list_top">15dp</item> + <item type="dimen" name="aerr_padding_list_bottom">8dp</item> <item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item> diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml index ad2b335de5bb..00e48a0eb6e9 100644 --- a/core/res/res/values/dimens_material.xml +++ b/core/res/res/values/dimens_material.xml @@ -117,13 +117,13 @@ <dimen name="dialog_padding_material">24dp</dimen> <dimen name="dialog_padding_top_material">18dp</dimen> + <dimen name="dialog_title_divider_material">8dp</dimen> + <dimen name="dialog_list_padding_top_no_title">8dp</dimen> + <dimen name="dialog_list_padding_bottom_no_buttons">8dp</dimen> <!-- Dialog padding minus control padding, used to fix alignment. --> <dimen name="select_dialog_padding_start_material">20dp</dimen> - <!-- Padding above and below selection dialog lists. --> - <dimen name="dialog_list_padding_vertical_material">8dp</dimen> - <dimen name="seekbar_track_background_height_material">2dp</dimen> <dimen name="seekbar_track_progress_height_material">2dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b0726d28ab6d..7d19e99f65eb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1766,6 +1766,7 @@ <java-symbol type="integer" name="config_shutdownBatteryTemperature" /> <java-symbol type="integer" name="config_undockedHdmiRotation" /> <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" /> + <java-symbol type="integer" name="config_brightness_ramp_rate_fast" /> <java-symbol type="layout" name="am_compat_mode_dialog" /> <java-symbol type="layout" name="launch_warning" /> <java-symbol type="layout" name="safe_mode" /> @@ -2451,24 +2452,6 @@ <java-symbol type="id" name="aerr_close" /> <java-symbol type="id" name="aerr_mute" /> - <!-- Framework-private Material.DayNight styles. --> - <java-symbol type="style" name="Theme.Material.DayNight" /> - <java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" /> - <java-symbol type="style" name="Theme.Material.DayNight.Dialog" /> - <java-symbol type="style" name="Theme.Material.DayNight.Dialog.Alert" /> - <java-symbol type="style" name="Theme.Material.DayNight.Dialog.MinWidth" /> - <java-symbol type="style" name="Theme.Material.DayNight.Dialog.NoActionBar" /> - <java-symbol type="style" name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" /> - <java-symbol type="style" name="Theme.Material.DayNight.Dialog.Presentation" /> - <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge" /> - <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" /> - <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar" /> - <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.Fullscreen" /> - <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.Overscan" /> - <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" /> - <java-symbol type="style" name="Theme.Material.DayNight.Panel" /> - <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" /> - <java-symbol type="string" name="status_bar_rotate" /> <java-symbol type="string" name="status_bar_headset" /> <java-symbol type="string" name="status_bar_data_saver" /> @@ -2556,4 +2539,7 @@ <!-- WallpaperManager config --> <java-symbol type="string" name="config_wallpaperCropperPackage" /> + + <java-symbol type="id" name="textSpacerNoTitle" /> + <java-symbol type="id" name="titleDividerNoCustom" /> </resources> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index a361eda8ae75..6611eb12c93a 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -1294,7 +1294,7 @@ please see themes_device_defaults.xml. </style> <!-- Default theme for Settings and activities launched from Settings. --> - <style name="Theme.Material.Settings" parent="Theme.Material.DayNight.DarkActionBar"> + <style name="Theme.Material.Settings" parent="Theme.Material.Light.DarkActionBar"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> @@ -1304,7 +1304,7 @@ please see themes_device_defaults.xml. </style> <!-- Default theme for Settings and activities launched from Settings. --> - <style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.DayNight.NoActionBar"> + <style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.Light.NoActionBar"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> @@ -1313,41 +1313,41 @@ please see themes_device_defaults.xml. <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item> </style> - <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.DayNight.BaseDialog"> + <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.Light.BaseDialog"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> <style name="Theme.Material.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog" /> - <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.DayNight.Dialog.BaseAlert"> + <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> <style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert" /> - <style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar"> + <style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> - <style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.DayNight.DialogWhenLarge.NoActionBar"> + <style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> - <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.DayNight.Dialog.Presentation"> + <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> - <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.DayNight.SearchBar"> + <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.Light.SearchBar"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> - <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.DayNight.CompactMenu"> + <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.Light.CompactMenu"> <item name="colorPrimary">@color/material_blue_grey_900</item> <item name="colorPrimaryDark">@color/material_blue_grey_950</item> </style> diff --git a/core/res/res/values/themes_material_daynight.xml b/core/res/res/values/themes_material_daynight.xml deleted file mode 100644 index 4ecca6be0b70..000000000000 --- a/core/res/res/values/themes_material_daynight.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 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. ---> - -<!-- -=============================================================== - PLEASE READ -=============================================================== - -The Material themes must not be modified in order to pass CTS. -Many related themes and styles depend on other values defined in this file. -If you would like to provide custom themes and styles for your device, -please see themes_device_defaults.xml. - -=============================================================== - PLEASE READ -=============================================================== - --> -<resources> - - <!-- Material theme (day/night vesion) for activities. --> - <style name="Theme.Material.DayNight" parent="Theme.Material.Light" /> - - <!-- Variant of Material.DayNight that has a solid (opaque) action bar - with an inverse color profile. The dark action bar sharply stands out against - the light content (when applicable). --> - <style name="Theme.Material.DayNight.DarkActionBar" parent="Theme.Material.Light.DarkActionBar" /> - - <!-- Variant of Material.DayNight with no action bar. --> - <style name="Theme.Material.DayNight.NoActionBar" parent="Theme.Material.Light.NoActionBar" /> - - <!-- Variant of Material.DayNight that has no title bar and fills - the entire screen. This theme - sets {@link android.R.attr#windowFullscreen} to true. --> - <style name="Theme.Material.DayNight.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen" /> - - <!-- Variant of Material.DayNight that has no title bar and fills - the entire screen and extends into the display overscan region. This theme - sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan} - to true. --> - <style name="Theme.Material.DayNight.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan" /> - - <!-- Variant of Material.DayNight that has no title bar and translucent - system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and - {@link android.R.attr#windowTranslucentNavigation} to true. --> - <style name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor" /> - - <!-- Default Material.DayNight theme for panel windows. This removes all extraneous - window decorations, so you basically have an empty rectangle in which - to place your content. It makes the window floating, with a transparent - background, and turns off dimming behind the window. --> - <style name="Theme.Material.DayNight.Panel" parent="Theme.Material.Light.Panel" /> - - <!-- Material theme (day/night vesion) for dialog windows and activities, - which is used by the {@link android.app.Dialog} class. This changes - the window to be floating (not fill the entire screen), and puts a - frame around its contents. You can set this theme on an activity if - you would like to make an activity that looks like a Dialog. --> - <style name="Theme.Material.DayNight.Dialog" parent="Theme.Material.DayNight.BaseDialog" /> - <style name="Theme.Material.DayNight.BaseDialog" parent="Theme.Material.Light.BaseDialog" /> - - <!-- Variant of Theme.Material.DayNight.Dialog that has a nice minimum width for - a regular dialog. --> - <style name="Theme.Material.DayNight.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth" /> - - <!-- Variant of Theme.Material.DayNight.Dialog that does not include a title bar. --> - <style name="Theme.Material.DayNight.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar" /> - - <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a nice minimum width for - a regular dialog. --> - <style name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth" /> - - <!-- Variant of Theme.Material.DayNight.Dialog that has a fixed size. --> - <style name="Theme.Material.DayNight.Dialog.FixedSize" parent="Theme.Material.Light.Dialog.FixedSize" /> - - <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a fixed size. --> - <style name="Theme.Material.DayNight.Dialog.NoActionBar.FixedSize" parent="Theme.Material.Light.Dialog.NoActionBar.FixedSize" /> - - <!-- Theme for a window that will be displayed either full-screen on - smaller screens (small, normal) or as a dialog on larger screens - (large, xlarge). --> - <style name="Theme.Material.DayNight.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge" /> - - <!-- Theme for a window with a dark action bar that will be displayed - either full-screen on smaller screens (small, normal) or as a dialog - on larger screens (large, xlarge). --> - <style name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar" /> - - <!-- Theme for a window without an action bar that will be displayed either full-screen - on smaller screens (small, normal) or as a dialog on larger screens - (large, xlarge). --> - <style name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar" /> - - <!-- Theme for a presentation window on a secondary display. --> - <style name="Theme.Material.DayNight.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation" /> - - <!-- Material user theme for alert dialog windows, which is used by the - {@link android.app.AlertDialog} class. --> - <style name="Theme.Material.DayNight.Dialog.Alert" parent="Theme.Material.DayNight.Dialog.BaseAlert" /> - <style name="Theme.Material.DayNight.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert" /> - - <style name="Theme.Material.DayNight.SearchBar" parent="Theme.Material.Light.SearchBar" /> - <style name="Theme.Material.DayNight.CompactMenu" parent="Theme.Material.Light.CompactMenu" /> - -</resources> diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java index 7519627bb0d0..2a24881f67fa 100644 --- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java @@ -22,7 +22,6 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.ActionMode; import android.view.ActionMode.Callback; import android.view.KeyEvent; -import android.view.KeyboardShortcutGroup; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index fd28f64db19e..e3639ecdeeb8 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -101,8 +101,13 @@ font_src_files := checkbuild: fontchain_lint FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py +ifeq ($(SMALLER_FONT_FOOTPRINT),true) +CHECK_EMOJI := false +else +CHECK_EMOJI := true +endif .PHONY: fontchain_lint fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \ - python $(FONTCHAIN_LINTER) $(TARGET_OUT) external/unicode + python $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 0a8c6929d54d..00eff9139dc8 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2157,7 +2157,7 @@ public class AudioManager { final RecordConfigChangeCallbackData cbData = (RecordConfigChangeCallbackData) msg.obj; if (cbData.mCb != null) { - cbData.mCb.onRecordConfigChanged(cbData.mConfigs); + cbData.mCb.onRecordingConfigChanged(cbData.mConfigs); } break; default: @@ -2746,7 +2746,7 @@ public class AudioManager { * @param configs array containing the results of * {@link AudioManager#getActiveRecordingConfigurations()}. */ - public void onRecordConfigChanged(AudioRecordingConfiguration[] configs) {} + public void onRecordingConfigChanged(AudioRecordingConfiguration[] configs) {} } private static class AudioRecordingCallbackInfo { diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index b6ff41ef15d0..621129d63f9c 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -596,14 +596,14 @@ public class AudioTrack implements AudioRouting * AudioTrack player = new AudioTrack.Builder() * .setAudioAttributes(new AudioAttributes.Builder() * .setUsage(AudioAttributes.USAGE_ALARM) - * .setContentType(CONTENT_TYPE_MUSIC) + * .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) * .build()) * .setAudioFormat(new AudioFormat.Builder() * .setEncoding(AudioFormat.ENCODING_PCM_16BIT) - * .setSampleRate(441000) + * .setSampleRate(44100) * .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO) * .build()) - * .setBufferSize(minBuffSize) + * .setBufferSizeInBytes(minBuffSize) * .build(); * </pre> * <p> diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 9cad00552f3d..3d7e74426b7c 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -161,9 +161,14 @@ public class ExifInterface { public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram"; /** Type is double. */ public static final String TAG_EXPOSURE_TIME = "ExposureTime"; - /** Type is String. */ + /** Type is double. */ public static final String TAG_F_NUMBER = "FNumber"; - /** Type is String. */ + /** + * Type is double. + * + * @deprecated use {@link #TAG_F_NUMBER} instead + */ + @Deprecated public static final String TAG_APERTURE = "FNumber"; /** Type is String. */ public static final String TAG_FILE_SOURCE = "FileSource"; @@ -185,9 +190,14 @@ public class ExifInterface { public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution"; /** Type is rational. */ public static final String TAG_GAIN_CONTROL = "GainControl"; - /** Type is String. */ + /** Type is int. */ public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings"; - /** Type is String. */ + /** + * Type is int. + * + * @deprecated use {@link #TAG_ISO_SPEED_RATINGS} instead + */ + @Deprecated public static final String TAG_ISO = "ISOSpeedRatings"; /** Type is String. */ public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID"; @@ -225,8 +235,6 @@ public class ExifInterface { public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity"; /** Type is int. */ public static final String TAG_SUBSEC_TIME = "SubSecTime"; - /** Type is int. @hide */ - public static final String TAG_SUBSECTIME = "SubSecTime"; /** Type is int. */ public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized"; /** Type is int. */ @@ -437,7 +445,7 @@ public class ExifInterface { new ExifTag(TAG_F_NUMBER, 33437), new ExifTag(TAG_EXPOSURE_PROGRAM, 34850), new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852), - new ExifTag(TAG_ISO, 34855), + new ExifTag(TAG_ISO_SPEED_RATINGS, 34855), new ExifTag(TAG_OECF, 34856), new ExifTag(TAG_EXIF_VERSION, 36864), new ExifTag(TAG_DATETIME_ORIGINAL, 36867), @@ -1151,7 +1159,7 @@ public class ExifInterface { if (datetime == null) return -1; long msecs = datetime.getTime(); - String subSecs = getAttribute(TAG_SUBSECTIME); + String subSecs = getAttribute(TAG_SUBSEC_TIME); if (subSecs != null) { try { long sub = Long.valueOf(subSecs); @@ -1543,7 +1551,7 @@ public class ExifInterface { convertToDouble(TAG_EXPOSURE_TIME); convertToDouble(TAG_F_NUMBER); convertToDouble(TAG_SUBJECT_DISTANCE); - convertToInt(TAG_ISO); + convertToInt(TAG_ISO_SPEED_RATINGS); convertToDouble(TAG_EXPOSURE_BIAS_VALUE); convertToInt(TAG_WHITE_BALANCE); convertToInt(TAG_LIGHT_SOURCE); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index b1c1b79732c5..2bd978180c4c 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -2666,12 +2666,13 @@ public final class MediaCodecInfo { public static final int HEVCHighTierLevel62 = 0x2000000; // from OMX_VIDEO_DOLBYVISIONPROFILETYPE - public static final int DolbyVisionProfileDvavDer = 0x1; - public static final int DolbyVisionProfileDvavDen = 0x2; - public static final int DolbyVisionProfileDvheDer = 0x3; - public static final int DolbyVisionProfileDvheDen = 0x4; - public static final int DolbyVisionProfileDvheDtr = 0x5; - public static final int DolbyVisionProfileDvheStn = 0x6; + public static final int DolbyVisionProfileDvavPer = 0x1; + public static final int DolbyVisionProfileDvavPen = 0x2; + public static final int DolbyVisionProfileDvheDer = 0x4; + public static final int DolbyVisionProfileDvheDen = 0x8; + public static final int DolbyVisionProfileDvheDtr = 0x10; + public static final int DolbyVisionProfileDvheStn = 0x20; + public static final int DolbyVisionProfileDvheDth = 0x40; // from OMX_VIDEO_DOLBYVISIONLEVELTYPE public static final int DolbyVisionLevelHd24 = 0x1; diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index ebe509ce1a29..7117fbdbf122 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -34,7 +34,7 @@ import java.util.Map; /** * MediaMuxer facilitates muxing elementary streams. Currently supports mp4 or * webm file as the output and at most one audio and/or one video elementary - * stream. + * stream. MediaMuxer does not support muxing B-frames. * <p> * It is generally used like this: * diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index acf94f4cf7f6..03dc6995c798 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -116,10 +116,10 @@ public final class TvInputInfo implements Parcelable { private final int mType; private final boolean mIsHardwareInput; - // TODO: Remove mLabel and mIconUri when createTvInputInfo() is removed. - private String mLabel; + // TODO: Remove mIconUri when createTvInputInfo() is removed. private Uri mIconUri; + private final CharSequence mLabel; private final int mLabelResId; private final Icon mIcon; private final Icon mIconStandby; @@ -161,8 +161,8 @@ public final class TvInputInfo implements Parcelable { TvInputInfo info = new TvInputInfo.Builder(context, service) .setHdmiDeviceInfo(hdmiDeviceInfo) .setParentId(parentId) + .setLabel(label) .build(); - info.mLabel = label; info.mIconUri = iconUri; return info; } @@ -215,8 +215,8 @@ public final class TvInputInfo implements Parcelable { throws XmlPullParserException, IOException { TvInputInfo info = new TvInputInfo.Builder(context, service) .setTvInputHardwareInfo(hardwareInfo) + .setLabel(label) .build(); - info.mLabel = label; info.mIconUri = iconUri; return info; } @@ -247,7 +247,7 @@ public final class TvInputInfo implements Parcelable { } private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput, - int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected, + CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected, String setupActivity, String settingsActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) { @@ -255,6 +255,7 @@ public final class TvInputInfo implements Parcelable { mId = id; mType = type; mIsHardwareInput = isHardwareInput; + mLabel = label; mLabelResId = labelResId; mIcon = icon; mIconStandby = iconStandby; @@ -570,7 +571,7 @@ public final class TvInputInfo implements Parcelable { dest.writeString(mId); dest.writeInt(mType); dest.writeByte(mIsHardwareInput ? (byte) 1 : 0); - dest.writeString(mLabel); + TextUtils.writeToParcel(mLabel, dest, flags); dest.writeParcelable(mIconUri, flags); dest.writeInt(mLabelResId); dest.writeParcelable(mIcon, flags); @@ -612,7 +613,7 @@ public final class TvInputInfo implements Parcelable { mId = in.readString(); mType = in.readInt(); mIsHardwareInput = in.readByte() == 1; - mLabel = in.readString(); + mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mIconUri = in.readParcelable(null); mLabelResId = in.readInt(); mIcon = in.readParcelable(null); @@ -660,6 +661,7 @@ public final class TvInputInfo implements Parcelable { private final Context mContext; private final ResolveInfo mResolveInfo; + private CharSequence mLabel; private int mLabelResId; private Icon mIcon; private Icon mIconStandby; @@ -746,12 +748,31 @@ public final class TvInputInfo implements Parcelable { /** * Sets the label. * + * @param label The text to be used as label. + * @return This Builder object to allow for chaining of calls to builder methods. + * @hide + */ + @SystemApi + public Builder setLabel(CharSequence label) { + if (mLabelResId != 0) { + throw new IllegalStateException("Resource ID for label is already set."); + } + this.mLabel = label; + return this; + } + + /** + * Sets the label. + * * @param resId The resource ID of the text to use. * @return This Builder object to allow for chaining of calls to builder methods. * @hide */ @SystemApi public Builder setLabel(int resId) { + if (mLabel != null) { + throw new IllegalStateException("Label text is already set."); + } this.mLabelResId = resId; return this; } @@ -868,8 +889,8 @@ public final class TvInputInfo implements Parcelable { type = TYPE_TUNER; } parseServiceMetadata(type); - return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabelResId, mIcon, - mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity, + return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId, + mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity, mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount, mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java index dde9bdadebcd..312d9aaa8750 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java @@ -61,7 +61,7 @@ public class ExifInterfaceTest extends AndroidTestCase { private static final String[] EXIF_TAGS = { ExifInterface.TAG_MAKE, ExifInterface.TAG_MODEL, - ExifInterface.TAG_APERTURE, + ExifInterface.TAG_F_NUMBER, ExifInterface.TAG_DATETIME, ExifInterface.TAG_EXPOSURE_TIME, ExifInterface.TAG_FLASH, @@ -77,7 +77,7 @@ public class ExifInterfaceTest extends AndroidTestCase { ExifInterface.TAG_GPS_TIMESTAMP, ExifInterface.TAG_IMAGE_LENGTH, ExifInterface.TAG_IMAGE_WIDTH, - ExifInterface.TAG_ISO, + ExifInterface.TAG_ISO_SPEED_RATINGS, ExifInterface.TAG_ORIENTATION, ExifInterface.TAG_WHITE_BALANCE }; @@ -288,7 +288,7 @@ public class ExifInterfaceTest extends AndroidTestCase { // Checks values. assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make); assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model); - assertFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture); + assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture); assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime); assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime); assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash); @@ -308,7 +308,7 @@ public class ExifInterfaceTest extends AndroidTestCase { assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp); assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength); assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth); - assertStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso); + assertStringTag(exifInterface, ExifInterface.TAG_ISO_SPEED_RATINGS, expectedValue.iso); assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation); assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance); } diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml index b16554c5f90d..b0996aa6941c 100644 --- a/packages/DocumentsUI/res/values/styles.xml +++ b/packages/DocumentsUI/res/values/styles.xml @@ -36,8 +36,6 @@ <item name="android:windowActionBar">false</item> <item name="android:windowActionModeOverlay">true</item> <item name="android:windowNoTitle">true</item> - <item name="android:windowTranslucentStatus">true</item> - <item name="android:fitsSystemWindows">false</item> <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item> </style> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index 2d051e467ec3..87136ef35bc9 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -49,6 +49,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.WindowManager; import android.widget.Spinner; import com.android.documentsui.SearchViewManager.SearchManagerListener; @@ -103,6 +104,16 @@ public abstract class BaseActivity extends Activity @CallSuper @Override public void onCreate(Bundle icicle) { + // This flag is being set here as a result of the bug. When the flag was set in the + // styles.xml keyboard was messing the layout of dialogs (create dir, rename). + // Attempts were made to keep the flag in the main theme and to override it in the dialog + // layout xml or to create separate style for dialog and assign it in styles.xml. + // None of this brought successful results. + // Setting the flag works here most probably because of the timing when it is set. Also the + // setting might not affect the dialogs that are created in new windows or it affects them + // in the different way that having this in the style. + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // Record the time when onCreate is invoked for metric. mStartTime = new Date().getTime(); @@ -261,6 +272,7 @@ public abstract class BaseActivity extends Activity @Override public boolean onOptionsItemSelected(MenuItem item) { + Metrics.logMenuAction(this, item.getItemId()); switch (item.getItemId()) { case android.R.id.home: @@ -624,10 +636,12 @@ public abstract class BaseActivity extends Activity return true; } } else if (keyCode == KeyEvent.KEYCODE_TAB) { + Metrics.logKeyboardAction(this, keyCode); // Tab toggles focus on the navigation drawer. toggleNavDrawerFocus(); return true; } else if (keyCode == KeyEvent.KEYCODE_DEL) { + Metrics.logKeyboardAction(this, keyCode); popDir(); return true; } diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index 68c0c2a27f8b..d4439d8b480f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -212,19 +212,22 @@ public class FilesActivity extends BaseActivity { case R.id.menu_create_dir: assert(canCreateDirectory()); showCreateDirectoryDialog(); - return true; + break; case R.id.menu_new_window: createNewWindow(); - return true; + break; case R.id.menu_paste_from_clipboard: DirectoryFragment dir = getDirectoryFragment(); if (dir != null) { dir.pasteFromClipboard(); } - return true; + break; + default: + return super.onOptionsItemSelected(item); } - return super.onOptionsItemSelected(item); + Metrics.logMenuAction(this, item.getItemId()); + return true; } private void createNewWindow() { @@ -346,18 +349,21 @@ public class FilesActivity extends BaseActivity { case KeyEvent.KEYCODE_A: dir = getDirectoryFragment(); if (dir != null) { + Metrics.logKeyboardAction(this, keyCode); dir.selectAllFiles(); } return true; case KeyEvent.KEYCODE_C: dir = getDirectoryFragment(); if (dir != null) { + Metrics.logKeyboardAction(this, keyCode); dir.copySelectedToClipboard(); } return true; case KeyEvent.KEYCODE_V: dir = getDirectoryFragment(); if (dir != null) { + Metrics.logKeyboardAction(this, keyCode); dir.pasteFromClipboard(); } return true; diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java index f1f47c832d66..a4a67f96a3ee 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java @@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.provider.DocumentsContract; import android.util.Log; +import android.view.KeyEvent; import com.android.documentsui.State.ActionType; import com.android.documentsui.model.DocumentInfo; @@ -67,6 +68,10 @@ public final class Metrics { private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled"; private static final String COUNT_STARTUP_MS = "docsui_startup_ms"; private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened"; + private static final String COUNT_DRAG_N_DROP = "docsui_drag_n_drop"; + private static final String COUNT_SEARCH = "docsui_search"; + private static final String COUNT_MENU_ACTION = "docsui_menu_action"; + private static final String COUNT_KEYBOARD_ACTION = "docsui_keyboard_action"; // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any // root that is not explicitly recognized by the Metrics code (see {@link @@ -196,8 +201,71 @@ public final class Metrics { @Retention(RetentionPolicy.SOURCE) public @interface MetricsOpType {} - // Codes representing different launch actions. These are used for bucketing stats in the - // COUNT_LAUNCH_ACTION histogram. + // Codes representing different provider types. Used for sorting file operations when logging. + private static final int PROVIDER_INTRA = 0; + private static final int PROVIDER_SYSTEM = 1; + private static final int PROVIDER_EXTERNAL = 2; + + @IntDef(flag = false, value = { + PROVIDER_INTRA, + PROVIDER_SYSTEM, + PROVIDER_EXTERNAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Provider {} + + + // Codes representing different menu actions. These are used for bucketing stats in the + // COUNT_MENU_ACTION histogram. + // Both regular toolbar menu and action mode menu operations are included. + // Do not change or rearrange these values, that will break historical data. Only add to the + // list. + // Do not use negative numbers or zero; clearcut only handles positive integers. + private static final int ACTION_MENU_OTHER = 1; + private static final int ACTION_MENU_GRID = 2; + private static final int ACTION_MENU_LIST = 3; + private static final int ACTION_MENU_SORT = 4; + private static final int ACTION_MENU_SORT_NAME = 5; + private static final int ACTION_MENU_SORT_DATE = 6; + private static final int ACTION_MENU_SORT_SIZE = 7; + private static final int ACTION_MENU_SEARCH = 8; + private static final int ACTION_MENU_SHOW_SIZE = 9; + private static final int ACTION_MENU_SETTINGS = 10; + private static final int ACTION_MENU_COPY_TO = 11; + private static final int ACTION_MENU_MOVE_TO = 12; + private static final int ACTION_MENU_DELETE = 13; + private static final int ACTION_MENU_RENAME = 14; + private static final int ACTION_MENU_CREATE_DIR = 15; + private static final int ACTION_MENU_SELECT_ALL = 16; + private static final int ACTION_MENU_SHARE = 17; + private static final int ACTION_MENU_OPEN = 18; + private static final int ACTION_MENU_ADVANCED = 19; + + @IntDef(flag = false, value = { + ACTION_MENU_OTHER, + ACTION_MENU_GRID, + ACTION_MENU_LIST, + ACTION_MENU_SORT, + ACTION_MENU_SORT_NAME, + ACTION_MENU_SORT_DATE, + ACTION_MENU_SORT_SIZE, + ACTION_MENU_SHOW_SIZE, + ACTION_MENU_SETTINGS, + ACTION_MENU_COPY_TO, + ACTION_MENU_MOVE_TO, + ACTION_MENU_DELETE, + ACTION_MENU_RENAME, + ACTION_MENU_CREATE_DIR, + ACTION_MENU_SELECT_ALL, + ACTION_MENU_SHARE, + ACTION_MENU_OPEN, + ACTION_MENU_ADVANCED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MenuAction {} + + // Codes representing different menu actions. These are used for bucketing stats in the + // COUNT_MENU_ACTION histogram. // Do not change or rearrange these values, that will break historical data. Only add to the // list. // Do not use negative numbers or zero; clearcut only handles positive integers. @@ -223,18 +291,30 @@ public final class Metrics { @Retention(RetentionPolicy.SOURCE) public @interface MetricsAction {} - // Codes representing different provider types. Used for sorting file operations when logging. - private static final int PROVIDER_INTRA = 0; - private static final int PROVIDER_SYSTEM = 1; - private static final int PROVIDER_EXTERNAL = 2; - - @IntDef(flag = true, value = { - PROVIDER_INTRA, - PROVIDER_SYSTEM, - PROVIDER_EXTERNAL + // Codes representing different keyboard shortcut triggered actions. These are used for + // bucketing stats in the COUNT_KEYBOARD_ACTION histogram. + // Do not change or rearrange these values, that will break historical data. Only add to the + // list. + // Do not use negative numbers or zero; clearcut only handles positive integers. + private static final int ACTION_KEYBOARD_OTHER = 1; + private static final int ACTION_KEYBOARD_PASTE = 2; + private static final int ACTION_KEYBOARD_COPY = 3; + private static final int ACTION_KEYBOARD_DELETE = 4; + private static final int ACTION_KEYBOARD_SELECT_ALL = 5; + private static final int ACTION_KEYBOARD_BACK = 6; + private static final int ACTION_KEYBOARD_SWITCH_FOCUS = 7; + + @IntDef(flag = false, value = { + ACTION_KEYBOARD_OTHER, + ACTION_KEYBOARD_PASTE, + ACTION_KEYBOARD_COPY, + ACTION_KEYBOARD_DELETE, + ACTION_KEYBOARD_SELECT_ALL, + ACTION_KEYBOARD_BACK, + ACTION_KEYBOARD_SWITCH_FOCUS }) @Retention(RetentionPolicy.SOURCE) - public @interface Provider {} + public @interface KeyboardAction {} // Codes representing different actions to open the drawer. They are used for bucketing stats in // the COUNT_DRAWER_OPENED histogram. @@ -440,6 +520,39 @@ public final class Metrics { } /** + * Logs keyboard shortcut actions. Since keyboard shortcuts have their corresponding menu items, + * they are identified by menu item resource id for convenience. + * @param context + * @param keyCode + */ + public static void logKeyboardAction(Context context, int keyCode) { + @KeyboardAction int keyboardAction = ACTION_KEYBOARD_OTHER; + switch (keyCode) { + case KeyEvent.KEYCODE_V: + keyboardAction = ACTION_KEYBOARD_PASTE; + break; + case KeyEvent.KEYCODE_C: + keyboardAction = ACTION_KEYBOARD_COPY; + break; + case KeyEvent.KEYCODE_FORWARD_DEL: + keyboardAction = ACTION_KEYBOARD_DELETE; + break; + case KeyEvent.KEYCODE_A: + keyboardAction = ACTION_KEYBOARD_SELECT_ALL; + break; + case KeyEvent.KEYCODE_DEL: + keyboardAction = ACTION_KEYBOARD_BACK; + break; + case KeyEvent.KEYCODE_TAB: + keyboardAction = ACTION_KEYBOARD_SWITCH_FOCUS; + break; + default: + break; + } + logHistogram(context, COUNT_KEYBOARD_ACTION, keyboardAction); + } + + /** * Logs startup time in milliseconds. * @param context * @param startupMs Startup time in milliseconds. @@ -448,6 +561,25 @@ public final class Metrics { logHistogram(context, COUNT_STARTUP_MS, startupMs); } + /** + * Logs a drag and drop action. Call this when the user drops the content triggering copy. + * operation. + * + * @param context + */ + public static void logDragNDrop(Context context) { + logCount(context, COUNT_DRAG_N_DROP); + } + + /** + * Logs a search. Call this when the search operation is finished. + * + * @param context + */ + public static void logSearch(Context context) { + logCount(context, COUNT_SEARCH); + } + private static void logInterProviderFileOps( Context context, String histogram, @@ -568,6 +700,74 @@ public final class Metrics { } /** + * Logs menu action that was selected by user. + * @param context + * @param id Resource id of the menu item. + */ + public static void logMenuAction(Context context, int id) { + @MenuAction int menuAction = ACTION_MENU_OTHER; + switch (id) { + case R.id.menu_grid: + menuAction = ACTION_MENU_GRID; + break; + case R.id.menu_list: + menuAction = ACTION_MENU_LIST; + break; + case R.id.menu_sort: + menuAction = ACTION_MENU_SORT; + break; + case R.id.menu_sort_name: + menuAction = ACTION_MENU_SORT_NAME; + break; + case R.id.menu_sort_date: + menuAction = ACTION_MENU_SORT_DATE; + break; + case R.id.menu_sort_size: + menuAction = ACTION_MENU_SORT_SIZE; + break; + case R.id.menu_search: + menuAction = ACTION_MENU_SEARCH; + break; + case R.id.menu_file_size: + menuAction = ACTION_MENU_SHOW_SIZE; + break; + case R.id.menu_settings: + menuAction = ACTION_MENU_SETTINGS; + break; + case R.id.menu_copy_to: + menuAction = ACTION_MENU_COPY_TO; + break; + case R.id.menu_move_to: + menuAction = ACTION_MENU_MOVE_TO; + break; + case R.id.menu_delete: + menuAction = ACTION_MENU_DELETE; + break; + case R.id.menu_rename: + menuAction = ACTION_MENU_RENAME; + break; + case R.id.menu_create_dir: + menuAction = ACTION_MENU_CREATE_DIR; + break; + case R.id.menu_select_all: + menuAction = ACTION_MENU_SELECT_ALL; + break; + case R.id.menu_share: + menuAction = ACTION_MENU_SHARE; + break; + case R.id.menu_open: + menuAction = ACTION_MENU_OPEN; + break; + case R.id.menu_advanced: + menuAction = ACTION_MENU_ADVANCED; + break; + default: + break; + } + logHistogram(context, COUNT_MENU_ACTION, menuAction); + } + + /** * Internal method for making a MetricsLogger.count call. Increments the given counter by 1. * * @param context diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java index 11b8891b0b12..945ed3413ab6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java @@ -185,6 +185,9 @@ final class SearchViewManager implements if(mFullBar) { Menu menu = mActionBar.getMenu(); menu.setGroupVisible(R.id.group_hide_when_searching, false); + } else { + // If search in full-bar mode it will be logged in FilesActivity#onOptionsItemSelected + Metrics.logMenuAction(mActionBar.getContext(), R.id.menu_search); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 73ce0e1ffdc7..20316fff31b4 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -85,6 +85,7 @@ import com.android.documentsui.Events; import com.android.documentsui.Events.MotionInputEvent; import com.android.documentsui.Menus; import com.android.documentsui.MessageBar; +import com.android.documentsui.Metrics; import com.android.documentsui.R; import com.android.documentsui.RecentsLoader; import com.android.documentsui.RootsCache; @@ -594,6 +595,7 @@ public class DirectoryFragment extends Fragment @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + Metrics.logMenuAction(getContext(), item.getItemId()); Selection selection = mSelectionManager.getSelection(new Selection()); @@ -1124,6 +1126,7 @@ public class DirectoryFragment extends Fragment if (Objects.equals(src, dst)) { return false; } + Metrics.logDragNDrop(getContext()); copyFromClipData(event.getClipData(), dst); return true; } @@ -1336,6 +1339,7 @@ public class DirectoryFragment extends Fragment // This has to be handled here instead of in a keyboard shortcut, because // keyboard shortcuts all have to be modified with the 'Ctrl' key. if (mSelectionManager.hasSelection()) { + Metrics.logKeyboardAction(getContext(), keyCode); deleteDocuments(mSelectionManager.getSelection()); } // Always handle the key, even if there was nothing to delete. This is a @@ -1622,6 +1626,9 @@ public class DirectoryFragment extends Fragment @Override public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) { if (!isAdded()) return; + if (mSearchMode) { + Metrics.logSearch(getContext()); + } State state = getDisplayState(); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java index 5b53cafd843d..4c8dc00cc0ae 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java @@ -162,7 +162,9 @@ public class UiBot extends BaseBot { } UiObject findSearchViewIcon() { - return findObject("com.android.documentsui:id/menu_search", "android:id/search_button"); + return mContext.getResources().getBoolean(R.bool.full_bar_search_view) + ? findObject("com.android.documentsui:id/menu_search") + : findObject("com.android.documentsui:id/menu_search", "android:id/search_button"); } UiObject findActionModeBar() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java index 454221a6cf42..2b549f1a7fac 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java @@ -77,6 +77,7 @@ public class KeyguardSecurityModel { case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: + case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: return SecurityMode.Password; case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java index af4c34795917..0feda92322d0 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java @@ -244,9 +244,20 @@ public final class PdfManipulationService extends Service { ranges = PageRangeUtils.normalize(ranges); + int lastPageIdx = mEditor.getPageCount() - 1; + final int rangeCount = ranges.length; for (int i = rangeCount - 1; i >= 0; i--) { PageRange range = ranges[i]; + + // Ignore removal of pages that are outside the document + if (range.getEnd() > lastPageIdx) { + if (range.getStart() > lastPageIdx) { + continue; + } + range = new PageRange(range.getStart(), lastPageIdx); + } + for (int j = range.getEnd(); j >= range.getStart(); j--) { mEditor.removePage(j); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index dba6a8bfbe22..cde0fa3b4896 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -476,7 +476,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } private void onPrintDocumentError(String message) { - mProgressMessageController.cancel(); + setState(mProgressMessageController.cancel()); ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY); setState(STATE_UPDATE_FAILED); @@ -502,7 +502,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat Log.i(LOG_TAG, "onUpdateCanceled()"); } - mProgressMessageController.cancel(); + setState(mProgressMessageController.cancel()); ensurePreviewUiShown(); switch (mState) { @@ -524,7 +524,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat Log.i(LOG_TAG, "onUpdateCompleted()"); } - mProgressMessageController.cancel(); + setState(mProgressMessageController.cancel()); ensurePreviewUiShown(); // Update the print job with the info for the written document. The page @@ -572,7 +572,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat Log.i(LOG_TAG, "onUpdateFailed()"); } - mProgressMessageController.cancel(); + setState(mProgressMessageController.cancel()); ensureErrorUiShown(error, PrintErrorFragment.ACTION_RETRY); if (mState == STATE_CREATE_FILE_FAILED @@ -1169,6 +1169,18 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat doFinish(); } + /** + * Update the selected pages from the text field. + */ + private void updateSelectedPagesFromTextField() { + PageRange[] selectedPages = computeSelectedPages(); + if (!Arrays.equals(mSelectedPages, selectedPages)) { + mSelectedPages = selectedPages; + // Update preview. + updatePrintPreviewController(false); + } + } + private void confirmPrint() { setState(STATE_PRINT_CONFIRMED); @@ -1178,14 +1190,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat addCurrentPrinterToHistory(); setUserPrinted(); - PageRange[] selectedPages = computeSelectedPages(); - if (!Arrays.equals(mSelectedPages, selectedPages)) { - mSelectedPages = selectedPages; - // Update preview. - updatePrintPreviewController(false); - } - + // updateSelectedPagesFromTextField migth update the preview, hence apply the preview first updateSelectedPagesFromPreview(); + updateSelectedPagesFromTextField(); + mPrintPreviewController.closeOptions(); if (canUpdateDocument()) { @@ -1485,6 +1493,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat cancelPrint(); } } else if (view == mMoreOptionsButton) { + // The selected pages is only applied once the user leaves the text field. A click + // on this button, does not count as leaving. + updateSelectedPagesFromTextField(); + if (mCurrentPrinter != null) { startAdvancedPrintOptionsActivity(mCurrentPrinter); } @@ -2066,8 +2078,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mSpoolerProvider.destroy(); } + setState(mProgressMessageController.cancel()); + if (mState != STATE_INITIALIZING) { - mProgressMessageController.cancel(); mPrintedDocument.finish(); mPrintedDocument.destroy(); mPrintPreviewController.destroy(new Runnable() { @@ -2773,13 +2786,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } if (view == mPageRangeEditText && !hasFocus) { - PageRange[] selectedPages = computeSelectedPages(); - if (selectedPages != null && !Arrays.equals(mSelectedPages, selectedPages)) { - mSelectedPages = selectedPages; - - // Update preview. - updatePrintPreviewController(false); - } + updateSelectedPagesFromTextField(); } } } @@ -2910,29 +2917,50 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private boolean mPosted; + /** State before run was executed */ + private int mPreviousState = -1; + public ProgressMessageController(Context context) { mHandler = new Handler(context.getMainLooper(), null, false); } public void post() { - if (mPosted) { + if (mState == STATE_UPDATE_SLOW) { + setState(STATE_UPDATE_SLOW); + ensureProgressUiShown(); + updateOptionsUi(); + + return; + } else if (mPosted) { return; } + mPreviousState = -1; mPosted = true; mHandler.postDelayed(this, PROGRESS_TIMEOUT_MILLIS); } - public void cancel() { + private int getStateAfterCancel() { + if (mPreviousState == -1) { + return mState; + } else { + return mPreviousState; + } + } + + public int cancel() { if (!mPosted) { - return; + return getStateAfterCancel(); } mPosted = false; mHandler.removeCallbacks(this); + + return getStateAfterCancel(); } @Override public void run() { mPosted = false; + mPreviousState = mState; setState(STATE_UPDATE_SLOW); ensureProgressUiShown(); updateOptionsUi(); @@ -3081,12 +3109,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat List<PageRange> rangesToShred = new ArrayList<>(); PageRange previousRange = null; - final int pageCount = printJob.getDocumentInfo().getPageCount(); - PageRange[] printedPages = printJob.getPages(); final int rangeCount = printedPages.length; for (int i = 0; i < rangeCount; i++) { - PageRange range = PageRangeUtils.asAbsoluteRange(printedPages[i], pageCount); + PageRange range = printedPages[i]; if (previousRange == null) { final int startPageIdx = 0; @@ -3105,11 +3131,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } if (i == rangeCount - 1) { - final int startPageIdx = range.getEnd() + 1; - final int endPageIdx = printJob.getDocumentInfo().getPageCount() - 1; - if (startPageIdx <= endPageIdx) { - PageRange removedRange = new PageRange(startPageIdx, endPageIdx); - rangesToShred.add(removedRange); + if (range.getEnd() != Integer.MAX_VALUE) { + rangesToShred.add(new PageRange(range.getEnd() + 1, Integer.MAX_VALUE)); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 987b5ea2db09..61c9f2b15c82 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -21,6 +21,7 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.app.backup.BackupManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; @@ -63,6 +64,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.providers.settings.SettingsState.Setting; +import com.android.server.SystemConfig; import java.io.File; import java.io.FileDescriptor; @@ -1940,7 +1942,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 125; + private static final int SETTINGS_VERSION = 126; private final int mUserId; @@ -2136,6 +2138,35 @@ public class SettingsProvider extends ContentProvider { currentVersion = 125; } + if (currentVersion == 125) { + // Version 125: Allow OEMs to set the default VR service. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + + Setting currentSetting = secureSettings.getSettingLocked( + Settings.Secure.ENABLED_VR_LISTENERS); + if (currentSetting == null) { + ArraySet<ComponentName> l = + SystemConfig.getInstance().getDefaultVrComponents(); + + if (l != null && !l.isEmpty()) { + StringBuilder b = new StringBuilder(); + boolean start = true; + for (ComponentName c : l) { + if (!start) { + b.append(':'); + } + b.append(c.flattenToString()); + start = false; + } + secureSettings.insertSettingLocked( + Settings.Secure.ENABLED_VR_LISTENERS, b.toString(), + SettingsState.SYSTEM_PACKAGE_NAME); + } + + } + currentVersion = 126; + } + // vXXX: Add new settings above this point. // Return the current version. diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0c35573b9ee4..b557dc4d8671 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -139,7 +139,7 @@ <activity android:name=".BugreportWarningActivity" - android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert" + android:theme="@android:style/Theme.Material.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" android:exported="false" /> diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml index 89e4aac8dae5..ebc6a4a74a85 100644 --- a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml +++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml @@ -17,13 +17,13 @@ <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator - android:propertyName="translationY" - android:valueTo="10dp" - android:interpolator="@android:interpolator/linear_out_slow_in" + android:propertyName="scaleX" + android:valueTo="1.0" + android:interpolator="@android:interpolator/fast_out_slow_in" android:duration="@integer/recents_tv_pip_focus_anim_duration" /> <objectAnimator - android:propertyName="alpha" + android:propertyName="scaleY" android:valueTo="1.0" - android:interpolator="@android:interpolator/linear_out_slow_in" + android:interpolator="@android:interpolator/fast_out_slow_in" android:duration="@integer/recents_tv_pip_focus_anim_duration" /> </set> diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml index c73fed6d4892..95499bd38977 100644 --- a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml +++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml @@ -17,13 +17,13 @@ <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator - android:propertyName="translationY" - android:valueTo="0dp" - android:interpolator="@android:interpolator/fast_out_linear_in" + android:propertyName="scaleX" + android:valueTo="0.7" + android:interpolator="@android:interpolator/fast_out_slow_in" android:duration="@integer/recents_tv_pip_focus_anim_duration" /> <objectAnimator - android:propertyName="alpha" - android:valueTo="0.0" - android:interpolator="@android:interpolator/fast_out_linear_in" + android:propertyName="scaleY" + android:valueTo="0.7" + android:interpolator="@android:interpolator/fast_out_slow_in" android:duration="@integer/recents_tv_pip_focus_anim_duration" /> </set> diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml new file mode 100644 index 000000000000..7555bdda8b64 --- /dev/null +++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="translationY" + android:valueTo="0dp" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="@integer/recents_tv_pip_focus_anim_duration" /> diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml new file mode 100644 index 000000000000..b40ccd467bec --- /dev/null +++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="translationY" + android:valueTo="-57dp" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="@integer/recents_tv_pip_focus_anim_duration" /> diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml new file mode 100644 index 000000000000..681ff917b646 --- /dev/null +++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="alpha" + android:valueTo="1" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="@integer/recents_tv_pip_focus_anim_duration" /> diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml new file mode 100644 index 000000000000..e6deb0f0bfdc --- /dev/null +++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="alpha" + android:valueTo="0" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="@integer/recents_tv_pip_focus_anim_duration" /> diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png Binary files differdeleted file mode 100644 index 7b0fcc7c104e..000000000000 --- a/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png Binary files differdeleted file mode 100644 index 73e9c96d36e4..000000000000 --- a/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png Binary files differdeleted file mode 100644 index a02e21ccf3bf..000000000000 --- a/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png Binary files differdeleted file mode 100644 index 4af2617ae72e..000000000000 --- a/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png Binary files differdeleted file mode 100644 index 24bdbb683a6f..000000000000 --- a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png Binary files differdeleted file mode 100644 index 6ecd2d3e5779..000000000000 --- a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png Binary files differdeleted file mode 100644 index 5e733ef4116e..000000000000 --- a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png Binary files differdeleted file mode 100644 index ecc2c833e2e2..000000000000 --- a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/stat_notify_image.xml b/packages/SystemUI/res/drawable/stat_notify_image.xml new file mode 100644 index 000000000000..c8745d7d046c --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_notify_image.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M21,19V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14C20.1,21,21,20.1,21,19z +M8.5,13.5l2.5,3l3.5-4.5l4.5,6H5 L8.5,13.5z" /> + <path + android:pathData="M0,0h24v24H0V0z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_notify_image_error.xml b/packages/SystemUI/res/drawable/stat_notify_image_error.xml new file mode 100644 index 000000000000..b9290051a32b --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_notify_image_error.xml @@ -0,0 +1,30 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:pathData="M0,0h24v24H0V0z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17,18H5l3.5-4.5l2.5,3l3.3-4.5l2.7,3.8V8h4V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h12V18z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M19.2,21H21v-1.8h-1.8V21z M19.2,9.9v7.4H21V9.9H19.2z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml index 5cabb77a6971..405ea0cb9fbc 100644 --- a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml +++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml @@ -17,8 +17,8 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size - android:width="36dp" - android:height="36dp" /> + android:width="34dp" + android:height="34dp" /> <solid android:color="#4DFFFFFF" /> </shape> diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml index 0f8c77c3042b..28ea66d8345d 100644 --- a/packages/SystemUI/res/layout/recents_on_tv.xml +++ b/packages/SystemUI/res/layout/recents_on_tv.xml @@ -31,18 +31,12 @@ android:focusable="true" android:layoutDirection="rtl" /> + <!-- Placeholder view to give focus to the PIP menus. --> <View - android:id="@+id/pip_shade" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone" - android:background="#76000000" /> - - <include - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:layout_marginTop="132dp" - layout="@layout/tv_pip_controls" /> + android:id="@+id/pip" + android:layout_width="0dp" + android:layout_height="0dp" + android:focusable="true" + android:visibility="gone" /> </com.android.systemui.recents.tv.views.RecentsTvView> diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml index 2b2ce4e53483..625e9c111672 100644 --- a/packages/SystemUI/res/layout/recents_stack_action_button.xml +++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml @@ -18,7 +18,6 @@ android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="start|center_vertical" android:paddingStart="14dp" android:paddingEnd="14dp" android:paddingTop="12dp" diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml index 2e0c9e76c504..563441ff3e35 100644 --- a/packages/SystemUI/res/layout/tv_pip_controls.xml +++ b/packages/SystemUI/res/layout/tv_pip_controls.xml @@ -17,13 +17,8 @@ */ --> -<com.android.systemui.tv.pip.PipControlsView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/pip_controls" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:orientation="horizontal"> +<!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. --> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="100dp" @@ -98,4 +93,4 @@ android:textSize="12sp" android:textColor="#EEEEEE" /> </LinearLayout> -</com.android.systemui.tv.pip.PipControlsView> +</merge> diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml index c2c83ffb9d62..2647a99632c1 100644 --- a/packages/SystemUI/res/layout/tv_pip_menu.xml +++ b/packages/SystemUI/res/layout/tv_pip_menu.xml @@ -26,7 +26,8 @@ android:gravity="top|center_horizontal" android:clipChildren="false"> - <include - layout="@layout/tv_pip_controls" - android:clipChildren="false" /> + <com.android.systemui.tv.pip.PipControlsView + android:id="@+id/pip_controls" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml index c5c7e843208b..64bf3b5bbdbf 100644 --- a/packages/SystemUI/res/layout/tv_pip_overlay.xml +++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml @@ -38,25 +38,4 @@ android:gravity="center" android:maxLines="2" android:text="@string/pip_hold_home" /> - <LinearLayout - android:id="@+id/guide_buttons" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" - android:orientation="horizontal"> - <ImageView - android:layout_width="19dp" - android:layout_height="19dp" - android:src="@drawable/ic_fullscreen_white_24dp" /> - <ImageView - android:layout_width="19dp" - android:layout_height="19dp" - android:src="@drawable/ic_close_white" /> - <ImageView - android:id="@+id/guide_button_play_pause" - android:layout_width="19dp" - android:layout_height="19dp" - android:src="@drawable/ic_pause_white_24dp" /> - </LinearLayout> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml new file mode 100644 index 000000000000..1e464d83ebdd --- /dev/null +++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="top|center_horizontal"> + + <com.android.systemui.tv.pip.PipRecentsControlsView + android:id="@+id/pip_controls" + android:layout_width="wrap_content" + android:layout_height="match_parent" /> + + <View + android:id="@+id/recents" + android:layout_width="1dp" + android:layout_height="1dp" + android:focusable="true" /> +</LinearLayout> diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml index 953dd650b2aa..337513d4daea 100644 --- a/packages/SystemUI/res/values/dimens_tv.xml +++ b/packages/SystemUI/res/values/dimens_tv.xml @@ -38,9 +38,6 @@ <dimen name="recents_tv_unselected_item_z">6dp</dimen> <dimen name="recents_tv_selected_item_z_delta">10dp</dimen> - <!-- Extra space around the PIP and its outline in PIP onboarding activity --> - <dimen name="tv_pip_bounds_space">3dp</dimen> - <!-- Values for text on recents cards on tv --> <dimen name="recents_tv_title_text_size">12sp</dimen> @@ -52,4 +49,10 @@ <dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen> <dimen name="recents_tv_dismiss_text_size">12sp</dimen> + <!-- Values for PIP in recents --> + <dimen name="recents_tv_pip_controls_margin_top">10dp</dimen> + + <!-- Extra space around the PIP and its outline in PIP onboarding activity --> + <dimen name="tv_pip_bounds_space">3dp</dimen> + </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a523a411399f..a01066d70262 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1388,6 +1388,31 @@ <string name="keyboard_shortcut_group_system_recents">Recents</string> <!-- User visible title for the the keyboard shortcut that triggers the back action. --> <string name="keyboard_shortcut_group_system_back">Back</string> + <!-- User visible title for the the keyboard shortcut that triggers the notification shade. --> + <string name="keyboard_shortcut_group_system_notifications">Notifications</string> + <!-- User visible title for the the keyboard shortcut that triggers the keyboard shortcuts helper. --> + <string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string> + <!-- User visible title for the the keyboard shortcut that switches input methods. --> + <string name="keyboard_shortcut_group_system_switch_input">Switch input method</string> + + <!-- User visible title for the system-wide applications keyboard shortcuts list. --> + <string name="keyboard_shortcut_group_applications">Applications</string> + <!-- User visible title for the keyboard shortcut that takes the user to the assist app. --> + <string name="keyboard_shortcut_group_applications_assist">Assist</string> + <!-- User visible title for the keyboard shortcut that takes the user to the browser app. --> + <string name="keyboard_shortcut_group_applications_browser">Browser</string> + <!-- User visible title for the keyboard shortcut that takes the user to the contacts app. --> + <string name="keyboard_shortcut_group_applications_contacts">Contacts</string> + <!-- User visible title for the keyboard shortcut that takes the user to the email app. --> + <string name="keyboard_shortcut_group_applications_email">Email</string> + <!-- User visible title for the keyboard shortcut that takes the user to the instant messaging app. --> + <string name="keyboard_shortcut_group_applications_im">IM</string> + <!-- User visible title for the keyboard shortcut that takes the user to the music app. --> + <string name="keyboard_shortcut_group_applications_music">Music</string> + <!-- User visible title for the keyboard shortcut that takes the user to the YouTube app. --> + <string name="keyboard_shortcut_group_applications_youtube">YouTube</string> + <!-- User visible title for the keyboard shortcut that takes the user to the calendar app. --> + <string name="keyboard_shortcut_group_applications_calendar">Calendar</string> <!-- SysUI Tuner: Option to show full do not disturb panel in volume [CHAR LIMIT=60] --> <string name="tuner_full_zen_title">Show with volume controls</string> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index fff7d78d0078..8060e07801bb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -344,7 +344,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD // Update the nav bar scrim, but defer the animation until the enter-window event boolean animateNavBarScrim = !launchState.launchedViaDockGesture; - updateNavBarScrim(animateNavBarScrim, null); + mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null); // If this is a new instance relaunched by AM, without going through the normal mechanisms, // then we have to manually trigger the enter animation state @@ -417,39 +417,43 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - // Update the nav bar for the current orientation - updateNavBarScrim(false /* animateNavBarScrim */, AnimationProps.IMMEDIATE); // Notify of the config change int newOrientation = getResources().getConfiguration().orientation; + int numStackTasks = mRecentsView.getStack().getStackTaskCount(); EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */, - (mLastOrientation != newOrientation))); + (mLastOrientation != newOrientation), numStackTasks > 0)); mLastOrientation = newOrientation; } @Override public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { super.onMultiWindowModeChanged(isInMultiWindowMode); + + // Reload the task stack completely + RecentsConfiguration config = Recents.getConfiguration(); + RecentsActivityLaunchState launchState = config.getLaunchState(); + RecentsTaskLoader loader = Recents.getTaskLoader(); + RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); + loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */); + + RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); + loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; + loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; + loader.loadTasks(this, loadPlan, loadOpts); + + TaskStack stack = loadPlan.getTaskStack(); + int numStackTasks = stack.getStackTaskCount(); + EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */, - false /* fromOrientationChange */)); + false /* fromOrientationChange */, numStackTasks > 0)); if (mRecentsView != null) { - // Reload the task stack completely - RecentsConfiguration config = Recents.getConfiguration(); - RecentsActivityLaunchState launchState = config.getLaunchState(); - RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); - loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */); - - RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); - loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; - loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; - loader.loadTasks(this, loadPlan, loadOpts); - - mRecentsView.updateStack(loadPlan.getTaskStack()); + mRecentsView.updateStack(stack); } - EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode)); + EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, + numStackTasks > 0)); } @Override @@ -729,18 +733,4 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD }); return true; } - - /** - * Updates the nav bar scrim. - */ - private void updateNavBarScrim(boolean animateNavBarScrim, AnimationProps animation) { - // Animate the SystemUI scrims into view - SystemServicesProxy ssp = Recents.getSystemServices(); - int taskCount = mRecentsView.getStack().getTaskCount(); - boolean hasNavBarScrim = (taskCount > 0) && !ssp.hasTransposedNavBar(); - mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim); - if (animateNavBarScrim && animation != null) { - mScrimViews.animateNavBarScrimVisibility(true, animation); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 43d627d9e13a..4dae7460f30a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -584,18 +584,24 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener Rect systemInsets = new Rect(); ssp.getStableInsets(systemInsets); Rect windowRect = ssp.getWindowRect(); + // When docked, the nav bar insets are consumed and the activity is measured without insets. + // However, the window bounds include the insets, so we need to subtract them here to make + // them identical. + if (ssp.hasDockedTask()) { + windowRect.bottom -= systemInsets.bottom; + systemInsets.bottom = 0; + } calculateWindowStableInsets(systemInsets, windowRect); windowRect.offsetTo(0, 0); TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm(); - stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right, - mTaskStackBounds); // Rebind the header bar and draw it for the transition - Rect taskStackBounds = new Rect(mTaskStackBounds); stackLayout.setSystemInsets(systemInsets); if (stack != null) { - stackLayout.initialize(windowRect, taskStackBounds, + stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right, + mTaskStackBounds); + stackLayout.initialize(windowRect, mTaskStackBounds, TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack)); mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */); } @@ -794,6 +800,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener synchronized (mHeaderBarLock) { int toHeaderWidth = (int) toTransform.rect.width(); int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale); + if (toHeaderWidth <= 0 || toHeaderHeight <= 0) { + return null; + } boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode(); mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(), (int) toTransform.rect.height()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java index 8be9ca7b5f2b..e3bc2a760b5f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java @@ -25,9 +25,12 @@ public class ConfigurationChangedEvent extends EventBus.AnimatedEvent { public final boolean fromMultiWindow; public final boolean fromOrientationChange; + public final boolean hasStackTasks; - public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromOrientationChange) { + public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromOrientationChange, + boolean hasStackTasks) { this.fromMultiWindow = fromMultiWindow; this.fromOrientationChange = fromOrientationChange; + this.hasStackTasks = hasStackTasks; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java index 19245d9534df..cf2a68e2cf95 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java @@ -24,8 +24,10 @@ import com.android.systemui.recents.events.EventBus; public class MultiWindowStateChangedEvent extends EventBus.Event { public final boolean inMultiWindow; + public final boolean hasStackTasks; - public MultiWindowStateChangedEvent(boolean inMultiWindow) { + public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean hasStackTasks) { this.inMultiWindow = inMultiWindow; + this.hasStackTasks = hasStackTasks; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java index b85ddac62b19..216be6121f8d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java @@ -23,7 +23,7 @@ import com.android.systemui.recents.views.DropTarget; /** * This event is sent when a user drags in/out of a drop target. */ -public class DragDropTargetChangedEvent extends EventBus.Event { +public class DragDropTargetChangedEvent extends EventBus.AnimatedEvent { // The task that is currently being dragged public final Task task; diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java index 134b90c99fff..483f9e523c55 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java @@ -15,8 +15,6 @@ */ package com.android.systemui.recents.tv; -import android.animation.AnimatorInflater; -import android.animation.AnimatorSet; import android.app.Activity; import android.app.ActivityOptions; import android.content.Intent; @@ -55,11 +53,12 @@ import com.android.systemui.recents.model.RecentsTaskLoadPlan; import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.tv.animations.FocusAnimationHolder; import com.android.systemui.recents.tv.views.RecentsTvView; import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.tv.pip.PipManager; -import com.android.systemui.tv.pip.PipControlsView; +import com.android.systemui.tv.pip.PipRecentsOverlayManager; import java.util.ArrayList; import java.util.Collections; @@ -80,15 +79,13 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { private boolean mIgnoreAltTabRelease; private RecentsTvView mRecentsView; - private PipControlsView mPipControlsView; - private View mPipShadeView; - private AnimatorSet mPipControlsViewFadeInAnimator; - private AnimatorSet mPipControlsViewFadeOutAnimator; + private FocusAnimationHolder mRecentsFocusAnimationHolder; + private View mPipView; private TaskStackHorizontalViewAdapter mTaskStackViewAdapter; private FinishRecentsRunnable mFinishLaunchHomeRunnable; - private PipManager mPipManager; - private PipManager.Listener mPipListener = new PipManager.Listener() { + private final PipManager mPipManager = PipManager.getInstance(); + private final PipManager.Listener mPipListener = new PipManager.Listener() { @Override public void onPipEntered() { updatePipUI(); @@ -113,10 +110,38 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { @Override public void onPipResizeAboutToStart() { } - - @Override - public void onMediaControllerChanged() { } }; + private PipRecentsOverlayManager mPipRecentsOverlayManager; + private final PipRecentsOverlayManager.Callback mPipRecentsOverlayManagerCallback = + new PipRecentsOverlayManager.Callback() { + @Override + public void onClosed() { + dismissRecentsToLaunchTargetTaskOrHome(); + } + + @Override + public void onBackPressed() { + RecentsTvActivity.this.onBackPressed(); + } + + @Override + public void onRecentsFocused() { + mRecentsView.requestFocus(); + } + }; + private final View.OnFocusChangeListener mPipViewFocusChangeListener = + new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + mRecentsFocusAnimationHolder.startFocusLoseAnimation(); + mPipRecentsOverlayManager.requestFocus( + mTaskStackViewAdapter.getItemCount() > 0); + } else { + mRecentsFocusAnimationHolder.startFocusGainAnimation(); + } + } + }; /** * A common Runnable to finish Recents by launching Home with an animation depending on the @@ -248,7 +273,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { finish(); return; } - mPipManager = PipManager.getInstance(); + mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager(); // Register this activity with the event bus EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); @@ -263,21 +288,19 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls); - mPipControlsView.setListener(new PipControlsView.Listener() { - @Override - public void onClosed() { - dismissRecentsToLaunchTargetTaskOrHome(); - } - }); - mPipShadeView = findViewById(R.id.pip_shade); + mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView); + + mPipView = findViewById(R.id.pip); + // Place mPipView at the PIP bounds for fine tuned focus handling. + Rect pipBounds = mPipManager.getPipBounds(); + LayoutParams lp = (LayoutParams) mPipView.getLayoutParams(); + lp.width = pipBounds.width(); + lp.height = pipBounds.height(); + lp.leftMargin = pipBounds.left; + lp.topMargin = pipBounds.top; + mPipView.setLayoutParams(lp); - mPipControlsViewFadeInAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this, - R.anim.tv_pip_controls_fade_in); - mPipControlsViewFadeInAnimator.setTarget(mPipControlsView); - mPipControlsViewFadeOutAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this, - R.anim.tv_pip_controls_fade_out); - mPipControlsViewFadeOutAnimator.setTarget(mPipControlsView); + mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback); getWindow().getAttributes().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; @@ -289,7 +312,6 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent); - updatePipUI(); mPipManager.addListener(mPipListener); } @@ -321,9 +343,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { SystemServicesProxy ssp = Recents.getSystemServices(); EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true)); - mPipManager.onRecentsStarted(); - // Give focus to the recents row whenever its visible to an user. - mRecentsView.requestFocus(); + updatePipUI(); } @Override @@ -333,10 +353,21 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { } @Override + public void onResume() { + super.onResume(); + mPipRecentsOverlayManager.onRecentsResumed(); + } + + @Override + public void onPause() { + super.onPause(); + mPipRecentsOverlayManager.onRecentsPaused(); + } + + @Override protected void onStop() { super.onStop(); - mPipManager.onRecentsStopped(); mIgnoreAltTabRelease = false; // Notify that recents is now hidden EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false)); @@ -480,25 +511,13 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { private void updatePipUI() { if (mPipManager.isPipShown()) { - mPipControlsView.setAlpha(0); - mPipControlsView.setVisibility(View.VISIBLE); - mPipShadeView.setVisibility(View.INVISIBLE); - mPipControlsView.setOnChildFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - mPipManager.onPipViewFocusChangedInRecents(hasFocus); - if (hasFocus) { - mPipControlsViewFadeInAnimator.start(); - } else { - mPipControlsViewFadeOutAnimator.start(); - } - mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE); - } - }); - mPipShadeView.setVisibility(View.GONE); + mPipView.setVisibility(View.VISIBLE); + mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener); + mPipView.requestFocus(); } else { - mPipControlsView.setVisibility(View.GONE); - mPipShadeView.setVisibility(View.GONE); + mPipView.setVisibility(View.GONE); + mPipRecentsOverlayManager.removePipRecentsOverlayView(); + mRecentsFocusAnimationHolder.reset(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java new file mode 100644 index 000000000000..864540c616e9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.recents.tv.animations; + +import android.content.res.Resources; +import android.view.View; + +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.recents.tv.views.TaskCardView; + +/** + * Collections of Recents row's animation depending on the PIP's focus. + */ +public class FocusAnimationHolder { + private final float DIM_ALPHA = 0.5f; + + private View mRecentsRowView; + private int mCardYDelta; + private long mDuration; + + public FocusAnimationHolder(View recentsRowView) { + mRecentsRowView = recentsRowView; + + Resources res = recentsRowView.getResources(); + mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down); + mDuration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration); + } + + public void startFocusGainAnimation() { + mRecentsRowView.animate() + .setDuration(mDuration) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .alpha(1f) + .translationY(0); + } + + public void startFocusLoseAnimation() { + mRecentsRowView.animate() + .setDuration(mDuration) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .alpha(DIM_ALPHA) + .translationY(mCardYDelta); + } + + public void reset() { + mRecentsRowView.setTransitionAlpha(1f); + mRecentsRowView.setTranslationY(0); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index da2f7212cae0..e0d048660b96 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -59,7 +59,6 @@ import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; import com.android.systemui.recents.events.activity.LaunchTaskEvent; import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; -import com.android.systemui.recents.events.ui.DeleteTaskDataEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; @@ -344,10 +343,10 @@ public class RecentsView extends FrameLayout { if (RecentsDebugFlags.Static.EnableStackActionButton) { // Measure the stack action button within the constraints of the space above the stack - Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect; + Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect; measureChild(mStackActionButton, - MeasureSpec.makeMeasureSpec(actionButtonRect.width(), MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(actionButtonRect.height(), MeasureSpec.AT_MOST)); + MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST)); } setMeasuredDimension(width, height); @@ -376,16 +375,9 @@ public class RecentsView extends FrameLayout { if (RecentsDebugFlags.Static.EnableStackActionButton) { // Layout the stack action button such that its drawable is start-aligned with the // stack, vertically centered in the available space above the stack - Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect; - int buttonLeft = isLayoutRtl() - ? actionButtonRect.right + mStackActionButton.getPaddingStart() - - mStackActionButton.getMeasuredWidth() - : actionButtonRect.left - mStackActionButton.getPaddingStart(); - int buttonTop = actionButtonRect.top + - (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2; - mStackActionButton.layout(buttonLeft, buttonTop, - buttonLeft + mStackActionButton.getMeasuredWidth(), - buttonTop + mStackActionButton.getMeasuredHeight()); + Rect buttonBounds = getStackActionButtonBoundsFromStackLayout(); + mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right, + buttonBounds.bottom); } if (mAwaitingFirstLayout) { @@ -479,6 +471,17 @@ public class RecentsView extends FrameLayout { false /* isDefaultDockState */, -1, true /* animateAlpha */, true /* animateBounds */); } + if (mStackActionButton != null) { + event.addPostAnimationCallback(new Runnable() { + @Override + public void run() { + // Move the clear all button to its new position + Rect buttonBounds = getStackActionButtonBoundsFromStackLayout(); + mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top, + buttonBounds.right, buttonBounds.bottom); + } + }); + } } public final void onBusEvent(final DragEndEvent event) { @@ -726,13 +729,31 @@ public class RecentsView extends FrameLayout { */ private void animateBackgroundScrim(float alpha, int duration) { Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator); - int alphaInt = (int) (alpha * 255); + // Calculate the absolute alpha to animate from + int fromAlpha = (int) ((mBackgroundScrim.getAlpha() / (DEFAULT_SCRIM_ALPHA * 255)) * 255); + int toAlpha = (int) (alpha * 255); mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA, - mBackgroundScrim.getAlpha(), alphaInt); + fromAlpha, toAlpha); mBackgroundScrimAnimator.setDuration(duration); - mBackgroundScrimAnimator.setInterpolator(alphaInt > mBackgroundScrim.getAlpha() - ? Interpolators.ALPHA_OUT - : Interpolators.ALPHA_IN); + mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha + ? Interpolators.ALPHA_IN + : Interpolators.ALPHA_OUT); mBackgroundScrimAnimator.start(); } + + /** + * @return the bounds of the stack action button. + */ + private Rect getStackActionButtonBoundsFromStackLayout() { + Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect); + int left = isLayoutRtl() + ? actionButtonRect.left - mStackActionButton.getPaddingLeft() + : actionButtonRect.right + mStackActionButton.getPaddingRight() + - mStackActionButton.getMeasuredWidth(); + int top = actionButtonRect.top + + (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2; + actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(), + top + mStackActionButton.getMeasuredHeight()); + return actionButtonRect; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index 13ad9a5ee90d..07a1d4edaeaf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -16,40 +16,58 @@ package com.android.systemui.recents.views; -import android.app.Activity; import android.content.Context; import android.view.View; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsActivity; +import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; +import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; +import com.android.systemui.recents.misc.SystemServicesProxy; /** Manages the scrims for the various system bars. */ public class SystemBarScrimViews { - Context mContext; + private static final int DEFAULT_ANIMATION_DURATION = 150; - View mNavBarScrimView; + private Context mContext; - boolean mHasNavBarScrim; - boolean mShouldAnimateNavBarScrim; + private View mNavBarScrimView; - int mNavBarScrimEnterDuration; + private boolean mHasNavBarScrim; + private boolean mShouldAnimateNavBarScrim; - public SystemBarScrimViews(Activity activity) { + private int mNavBarScrimEnterDuration; + + public SystemBarScrimViews(RecentsActivity activity) { mContext = activity; mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim); + mNavBarScrimView.forceHasOverlappingRendering(false); mNavBarScrimEnterDuration = activity.getResources().getInteger( R.integer.recents_nav_bar_scrim_enter_duration); } /** + * Updates the nav bar scrim. + */ + public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks, + AnimationProps animation) { + prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim); + if (animateNavBarScrim && animation != null) { + animateNavBarScrimVisibility(true, animation); + } + } + + /** * Prepares the scrim views for animating when entering Recents. This will be called before * the first draw, unless we are updating the scrim on configuration change. */ - public void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) { + private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) { mHasNavBarScrim = hasNavBarScrim; mShouldAnimateNavBarScrim = animateNavBarScrim; @@ -60,7 +78,7 @@ public class SystemBarScrimViews { /** * Animates the nav bar scrim visibility. */ - public void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) { + private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) { int toY = 0; if (visible) { mNavBarScrimView.setVisibility(View.VISIBLE); @@ -79,6 +97,14 @@ public class SystemBarScrimViews { } } + /** + * @return Whether to show the nav bar scrim. + */ + private boolean isNavBarScrimRequired(boolean hasStackTasks) { + SystemServicesProxy ssp = Recents.getSystemServices(); + return hasStackTasks && !ssp.hasTransposedNavBar() && !ssp.hasDockedTask(); + } + /**** EventBus events ****/ /** @@ -101,21 +127,48 @@ public class SystemBarScrimViews { */ public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { if (mHasNavBarScrim) { - AnimationProps animation = new AnimationProps() - .setDuration(AnimationProps.BOUNDS, - TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION) - .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN); + AnimationProps animation = createBoundsAnimation( + TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION); animateNavBarScrimVisibility(false, animation); } } public final void onBusEvent(DismissAllTaskViewsEvent event) { if (mHasNavBarScrim) { - AnimationProps animation = new AnimationProps() - .setDuration(AnimationProps.BOUNDS, - TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION) - .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN); + AnimationProps animation = createBoundsAnimation( + TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION); animateNavBarScrimVisibility(false, animation); } } + + public final void onBusEvent(ConfigurationChangedEvent event) { + animateScrimToCurrentNavBarState(event.hasStackTasks); + } + + public final void onBusEvent(MultiWindowStateChangedEvent event) { + animateScrimToCurrentNavBarState(event.hasStackTasks); + } + + /** + * Animates the scrim to match the state of the current nav bar. + */ + private void animateScrimToCurrentNavBarState(boolean hasStackTasks) { + boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks); + if (mHasNavBarScrim != hasNavBarScrim) { + AnimationProps animation = hasNavBarScrim + ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION) + : AnimationProps.IMMEDIATE; + animateNavBarScrimVisibility(hasNavBarScrim, animation); + } + mHasNavBarScrim = hasNavBarScrim; + } + + /** + * @return a default animation to aniamte the bounds of the scrim. + */ + private AnimationProps createBoundsAnimation(int duration) { + return new AnimationProps() + .setDuration(AnimationProps.BOUNDS, duration) + .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 25083040df1d..9eab0f60584f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -537,11 +537,13 @@ public class TaskStackLayoutAlgorithm { } else { mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP); } + mInitialNormX = null; } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) { // If there is one stack task, ignore the min/max/initial scroll positions mMinScrollP = 0; mMaxScrollP = 0; mInitialScrollP = 0; + mInitialNormX = null; } else { // Set the max scroll to be the point where the front most task is visible with the // stack bottom offset @@ -803,8 +805,9 @@ public class TaskStackLayoutAlgorithm { public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform frontTransform) { Rect windowRect = Recents.getSystemServices().getWindowRect(); - TaskViewTransform transform = getStackTransform(task, stackScroll, transformOut, - frontTransform); + TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState, + transformOut, frontTransform, true /* forceUpdate */, + false /* ignoreTaskOverrides */); transform.rect.offset(windowRect.left, windowRect.top); return transform; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index ddea4d98ae0e..a2f61c267f03 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -267,9 +267,9 @@ public class TaskViewHeader extends FrameLayout lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL); lp.setMarginStart(mHeaderBarHeight); - lp.rightMargin = mMoveTaskButton != null + lp.setMarginEnd(mMoveTaskButton != null ? 2 * mHeaderBarHeight - : mHeaderBarHeight; + : mHeaderBarHeight); title.setLayoutParams(lp); if (secondaryButton != null) { lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 3eeabc74801d..e5ac0d31ca8f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -141,9 +141,9 @@ public class TaskViewThumbnail extends View { return; } + int viewWidth = mTaskViewRect.width(); + int viewHeight = mTaskViewRect.height(); if (mBitmapShader != null) { - int viewWidth = mTaskViewRect.width(); - int viewHeight = mTaskViewRect.height(); // We are drawing the thumbnail in the same orientation, so just fit the width int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale); @@ -180,6 +180,9 @@ public class TaskViewThumbnail extends View { canvas.restoreToCount(count); } + } else { + canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius, + mBgFillPaint); } } @@ -319,12 +322,12 @@ public class TaskViewThumbnail extends View { mDisabledInSafeMode = disabledInSafeMode; if (t.thumbnail != null) { setThumbnail(t.thumbnail, thumbnailInfo); - if (t.colorBackground != 0) { - mBgFillPaint.setColor(t.colorBackground); - } } else { setThumbnail(null, null); } + if (t.colorBackground != 0) { + mBgFillPaint.setColor(t.colorBackground); + } } /** Unbinds the thumbnail view from the task */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 0e21517c3a60..4ed64260dc7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -19,7 +19,9 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; +import android.app.ActivityManager.StackId; import android.app.ActivityManagerNative; +import android.app.ActivityOptions; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -347,7 +349,7 @@ public abstract class BaseStatusBar extends SystemUI implements }, afterKeyguardGone); return true; } else { - return super.onClickHandler(view, pendingIntent, fillInIntent); + return superOnClickHandler(view, pendingIntent, fillInIntent); } } @@ -384,7 +386,8 @@ public abstract class BaseStatusBar extends SystemUI implements private boolean superOnClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { - return super.onClickHandler(view, pendingIntent, fillInIntent); + return super.onClickHandler(view, pendingIntent, fillInIntent, + StackId.FULLSCREEN_WORKSPACE_STACK_ID); } private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) { @@ -990,7 +993,7 @@ public abstract class BaseStatusBar extends SystemUI implements } TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) - .startActivities(null, + .startActivities(getActivityOptions(), new UserHandle(UserHandle.getUserId(appUid))); overrideActivityPendingAppTransition(keyguardShowing); } catch (RemoteException e) { @@ -1740,7 +1743,7 @@ public abstract class BaseStatusBar extends SystemUI implements } catch (RemoteException e) { } try { - intent.send(); + intent.send(null, 0, null, null, null, null, getActivityOptions()); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -1848,7 +1851,8 @@ public abstract class BaseStatusBar extends SystemUI implements } } try { - intent.send(); + intent.send(null, 0, null, null, null, null, + getActivityOptions()); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -1924,6 +1928,14 @@ public abstract class BaseStatusBar extends SystemUI implements } } + protected Bundle getActivityOptions() { + // Anything launched from the notification shade should always go into the + // fullscreen stack. + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID); + return options.toBundle(); + } + protected void visibilityChanged(boolean visible) { if (mVisible != visible) { mVisible = visible; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 8fe60a0d9d82..2b365dc743e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -245,7 +245,57 @@ public class KeyboardShortcuts { systemGroup.addItem(new KeyboardShortcutInfo( mContext.getString(R.string.keyboard_shortcut_group_system_recents), KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON)); + systemGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_system_notifications), + KeyEvent.KEYCODE_N, KeyEvent.META_META_ON)); + systemGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_system_shortcuts_helper), + KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON)); + systemGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_system_switch_input), + KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON)); result.add(systemGroup); + + KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup( + mContext.getString(R.string.keyboard_shortcut_group_applications), + true); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_assist), + KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_browser), + KeyEvent.KEYCODE_B, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_contacts), + KeyEvent.KEYCODE_C, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_email), + KeyEvent.KEYCODE_E, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_im), + KeyEvent.KEYCODE_T, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_music), + KeyEvent.KEYCODE_P, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_youtube), + KeyEvent.KEYCODE_Y, KeyEvent.META_META_ON)); + applicationGroup.addItem(new KeyboardShortcutInfo( + mContext.getString( + R.string.keyboard_shortcut_group_applications_calendar), + KeyEvent.KEYCODE_L, KeyEvent.META_META_ON)); + result.add(applicationGroup); + showKeyboardShortcutsDialog(result); } }, deviceId); @@ -354,11 +404,15 @@ public class KeyboardShortcuts { return null; } String displayLabelString; - if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) { + if (info.getBaseCharacter() > Character.MIN_VALUE) { displayLabelString = String.valueOf(info.getBaseCharacter()); } else if (SPECIAL_CHARACTER_NAMES.get(info.getKeycode()) != null) { displayLabelString = SPECIAL_CHARACTER_NAMES.get(info.getKeycode()); } else { + // Special case for shortcuts with no base key or keycode. + if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) { + return shortcutKeys; + } // TODO: Have a generic map for when we don't have the device's. char displayLabel = mKeyCharacterMap == null ? 0 : mKeyCharacterMap.getDisplayLabel(info.getKeycode()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 4c5c84321f51..c563eb606663 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -21,7 +21,9 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.ActivityManager.StackId; import android.app.ActivityManagerNative; +import android.app.ActivityOptions; import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; @@ -3091,8 +3093,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, null, mContext.getBasePackageName(), intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), - null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, - UserHandle.CURRENT.getIdentifier()); + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, + getActivityOptions(), UserHandle.CURRENT.getIdentifier()); } catch (RemoteException e) { Log.w(TAG, "Unable to start activity", e); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java index 15ad1f1b6099..3f87611fe2d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java @@ -16,11 +16,12 @@ package com.android.systemui.tv.pip; -import android.app.Activity; import android.content.Context; import android.media.session.MediaController; import android.media.session.PlaybackState; import android.view.View; +import android.view.Gravity; +import android.view.LayoutInflater; import android.view.View.OnFocusChangeListener; import android.widget.ImageView; import android.widget.TextView; @@ -40,28 +41,29 @@ import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE; /** * A view containing PIP controls including fullscreen, close, and media controls. */ -public class PipControlsView extends LinearLayout implements PipManager.Listener { +public class PipControlsView extends LinearLayout { /** * An interface to listen user action. */ - public interface Listener { + public abstract static interface Listener { /** * Called when an user clicks close PIP button. */ - void onClosed(); - } + public abstract void onClosed(); + }; - private final PipManager mPipManager = PipManager.getInstance(); private MediaController mMediaController; - private Listener mListener; - private View mFullButtonView; - private View mFullDescriptionView; - private View mPlayPauseView; - private ImageView mPlayPauseButtonImageView; - private TextView mPlayPauseDescriptionTextView; - private View mCloseButtonView; - private View mCloseDescriptionView; + final PipManager mPipManager = PipManager.getInstance(); + Listener mListener; + + View mFullButtonView; + View mFullDescriptionView; + View mPlayPauseView; + ImageView mPlayPauseButtonImageView; + TextView mPlayPauseDescriptionTextView; + View mCloseButtonView; + View mCloseDescriptionView; private boolean mHasFocus; private OnFocusChangeListener mOnChildFocusChangeListener; @@ -73,6 +75,13 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener } }; + private PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() { + @Override + public void onMediaControllerChanged() { + updateMediaController(); + } + }; + public PipControlsView(Context context) { this(context, null, 0, 0); } @@ -87,6 +96,12 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + LayoutInflater inflater = (LayoutInflater) getContext() + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.tv_pip_controls, this); + + setOrientation(LinearLayout.HORIZONTAL); + setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); } @Override @@ -161,13 +176,13 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener public void onAttachedToWindow() { super.onAttachedToWindow(); updateMediaController(); - mPipManager.addListener(this); + mPipManager.addMediaListener(mPipMediaListener); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - mPipManager.removeListener(this); + mPipManager.removeMediaListener(mPipMediaListener); if (mMediaController != null) { mMediaController.unregisterCallback(mMediaControllerCallback); } @@ -230,24 +245,4 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener public void setListener(Listener listener) { mListener = listener; } - - @Override - public void onPipEntered() { } - - @Override - public void onPipActivityClosed() { } - - @Override - public void onShowPipMenu() { } - - @Override - public void onMoveToFullscreen() { } - - @Override - public void onMediaControllerChanged() { - updateMediaController(); - } - - @Override - public void onPipResizeAboutToStart() { } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java index 68e0883fbc88..b5c1f5739e65 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java @@ -62,9 +62,27 @@ public class PipManager { private static final int MAX_RUNNING_TASKS_COUNT = 10; + /** + * State when there's no PIP. + */ public static final int STATE_NO_PIP = 0; + /** + * State when PIP is shown with an overlay message on top of it. + * This is used as default PIP state. + */ public static final int STATE_PIP_OVERLAY = 1; + /** + * State when PIP menu dialog is shown. + */ public static final int STATE_PIP_MENU = 2; + /** + * State when PIP is shown in Recents. + */ + public static final int STATE_PIP_RECENTS = 3; + /** + * State when PIP is shown in Recents and it's focused to allow an user to control. + */ + public static final int STATE_PIP_RECENTS_FOCUSED = 4; private static final int TASK_ID_NO_PIP = -1; private static final int INVALID_RESOURCE_TYPE = -1; @@ -90,11 +108,13 @@ public class PipManager { private int mSuspendPipResizingReason; private Context mContext; + private PipRecentsOverlayManager mPipRecentsOverlayManager; private IActivityManager mActivityManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; private final Handler mHandler = new Handler(); private List<Listener> mListeners = new ArrayList<>(); + private List<MediaListener> mMediaListeners = new ArrayList<>(); private Rect mCurrentPipBounds; private Rect mPipBounds; private Rect mMenuModePipBounds; @@ -107,9 +127,6 @@ public class PipManager { private MediaController mPipMediaController; private boolean mOnboardingShown; - private boolean mIsRecentsShown; - private boolean mIsPipFocusedInRecent; - private final Runnable mResizePinnedStackRunnable = new Runnable() { @Override public void run() { @@ -178,6 +195,7 @@ public class PipManager { mOnboardingShown = Prefs.getBoolean( mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false); + mPipRecentsOverlayManager = new PipRecentsOverlayManager(context); mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); } @@ -231,7 +249,7 @@ public class PipManager { /** * Moves the PIPed activity to the fullscreen and closes PIP system UI. */ - public void movePipToFullscreen() { + void movePipToFullscreen() { mState = STATE_NO_PIP; mPipTaskId = TASK_ID_NO_PIP; for (int i = mListeners.size() - 1; i >= 0; --i) { @@ -247,8 +265,11 @@ public class PipManager { */ private void showPipOverlay() { if (DEBUG) Log.d(TAG, "showPipOverlay()"); - mState = STATE_PIP_OVERLAY; - PipOverlayActivity.showPipOverlay(mContext); + Intent intent = new Intent(mContext, PipOverlayActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchStackId(PINNED_STACK_ID); + mContext.startActivity(intent, options.toBundle()); } /** @@ -279,8 +300,10 @@ public class PipManager { * Resize the Pip to the appropriate size for the input state. * @param state In Pip state also used to determine the new size for the Pip. */ - public void resizePinnedStack(int state) { + void resizePinnedStack(int state) { if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state); + boolean wasRecentsShown = + (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED); mState = state; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onPipResizeAboutToStart(); @@ -291,7 +314,6 @@ public class PipManager { mSuspendPipResizingReason); return; } - int animationDurationMs = -1; switch (mState) { case STATE_NO_PIP: mCurrentPipBounds = null; @@ -300,25 +322,24 @@ public class PipManager { mCurrentPipBounds = mMenuModePipBounds; break; case STATE_PIP_OVERLAY: - if (mIsRecentsShown) { - if (mCurrentPipBounds == mRecentsFocusedPipBounds - || mCurrentPipBounds == mRecentsFocusedPipBounds) { - animationDurationMs = mRecentsFocusChangedAnimationDurationMs; - } - if (mIsPipFocusedInRecent) { - mCurrentPipBounds = mRecentsFocusedPipBounds; - } else { - mCurrentPipBounds = mRecentsPipBounds; - } - } else { - mCurrentPipBounds = mPipBounds; - } + mCurrentPipBounds = mPipBounds; + break; + case STATE_PIP_RECENTS: + mCurrentPipBounds = mRecentsPipBounds; + break; + case STATE_PIP_RECENTS_FOCUSED: + mCurrentPipBounds = mRecentsFocusedPipBounds; break; default: mCurrentPipBounds = mPipBounds; break; } try { + int animationDurationMs = -1; + if (wasRecentsShown + && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) { + animationDurationMs = mRecentsFocusChangedAnimationDurationMs; + } mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true, animationDurationMs); } catch (RemoteException e) { @@ -327,67 +348,18 @@ public class PipManager { } /** - * Returns the current PIP bound for activities to sync their UI with PIP. + * Returns the default PIP bound. */ public Rect getPipBounds() { - return mCurrentPipBounds; + return mPipBounds; } /** - * Called when Recents is started. - * PIPed activity will be resized accordingly and overlay will show available buttons. + * Returns the focused PIP bound while Recents is shown. + * This is used to place PIP controls in Recents. */ - public void onRecentsStarted() { - mIsRecentsShown = true; - mIsPipFocusedInRecent = false; - if (mState == STATE_NO_PIP) { - return; - } - resizePinnedStack(STATE_PIP_OVERLAY); - } - - /** - * Called when Recents is stopped. - * PIPed activity will be resized accordingly and overlay will hide available buttons. - */ - public void onRecentsStopped() { - mIsRecentsShown = false; - mIsPipFocusedInRecent = false; - if (mState == STATE_NO_PIP) { - return; - } - resizePinnedStack(STATE_PIP_OVERLAY); - } - - /** - * Returns {@code true} if recents is shown. - */ - boolean isRecentsShown() { - return mIsRecentsShown; - } - - /** - * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity} - * is focused. - * This only resizes pinned stack so it looks like it's in Recents. - * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}. - */ - public void onPipViewFocusChangedInRecents(boolean hasFocus) { - mIsPipFocusedInRecent = hasFocus; - if (mState != STATE_PIP_OVERLAY) { - Log.w(TAG, "There is no pinned stack to handle focus change."); - return; - } - resizePinnedStack(STATE_PIP_OVERLAY); - } - - /** - * Returns {@code true} if the PIP view in - * {@link com.android.systemui.recents.tv.RecentsTvActivity} is focused in Recents. - * This API is valid only when {@link isRecentsShown()} returns {@code true}. - */ - boolean isPipViewFocusdInRecents() { - return mIsPipFocusedInRecent; + public Rect getRecentsFocusedPipBounds() { + return mRecentsFocusedPipBounds; } /** @@ -396,6 +368,10 @@ public class PipManager { */ private void showPipMenu() { if (DEBUG) Log.d(TAG, "showPipMenu()"); + if (mPipRecentsOverlayManager.isRecentsShown()) { + if (DEBUG) Log.d(TAG, "Ignore showing PIP menu"); + return; + } mState = STATE_PIP_MENU; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onShowPipMenu(); @@ -405,14 +381,34 @@ public class PipManager { mContext.startActivity(intent); } + /** + * Adds a {@link Listener} to PipManager. + */ public void addListener(Listener listener) { mListeners.add(listener); } + /** + * Removes a {@link Listener} from PipManager. + */ public void removeListener(Listener listener) { mListeners.remove(listener); } + /** + * Adds a {@link MediaListener} to PipManager. + */ + public void addMediaListener(MediaListener listener) { + mMediaListeners.add(listener); + } + + /** + * Removes a {@link MediaListener} from PipManager. + */ + public void removeMediaListener(MediaListener listener) { + mMediaListeners.remove(listener); + } + private void launchPipOnboardingActivityIfNeeded() { if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) { mOnboardingShown = true; @@ -485,8 +481,8 @@ public class PipManager { } if (mPipMediaController != mediaController) { mPipMediaController = mediaController; - for (int i = mListeners.size() - 1; i >= 0; i--) { - mListeners.get(i).onMediaControllerChanged(); + for (int i = mMediaListeners.size() - 1; i >= 0; i--) { + mMediaListeners.get(i).onMediaControllerChanged(); } if (mPipMediaController == null) { mHandler.postDelayed(mClosePipRunnable, @@ -530,7 +526,7 @@ public class PipManager { return PLAYBACK_STATE_UNAVAILABLE; } - TaskStackListener mTaskStackListener = new TaskStackListener() { + private TaskStackListener mTaskStackListener = new TaskStackListener() { @Override public void onTaskStackChanged() { if (mState != STATE_NO_PIP) { @@ -582,10 +578,10 @@ public class PipManager { mMediaSessionManager.addOnActiveSessionsChangedListener( mActiveMediaSessionListener, null); updateMediaController(mMediaSessionManager.getActiveSessions(null)); - if (mIsRecentsShown) { + if (mPipRecentsOverlayManager.isRecentsShown()) { // If an activity becomes PIPed again after the fullscreen, the Recents is shown // behind so we need to resize the pinned stack and show the correct overlay. - resizePinnedStack(STATE_PIP_OVERLAY); + resizePinnedStack(STATE_PIP_RECENTS); } for (int i = mListeners.size() - 1; i >= 0; i--) { mListeners.get(i).onPipEntered(); @@ -604,7 +600,18 @@ public class PipManager { if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()"); switch (mState) { case STATE_PIP_OVERLAY: - showPipOverlay(); + if (!mPipRecentsOverlayManager.isRecentsShown()) { + showPipOverlay(); + break; + } else { + // This happens only if an activity is PIPed after the Recents is shown. + // See {@link PipRecentsOverlayManager.requestFocus} for more details. + resizePinnedStack(mState); + break; + } + case STATE_PIP_RECENTS: + case STATE_PIP_RECENTS_FOCUSED: + mPipRecentsOverlayManager.addPipRecentsOverlayView(); break; case STATE_PIP_MENU: showPipMenu(); @@ -621,7 +628,7 @@ public class PipManager { * Invoked when an activity is pinned and PIP manager is set corresponding information. * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned} * because there's no guarantee for the PIP manager be return relavent information - * correctly. (e.g. {@link isPipShown}, {@link getPipBounds}) + * correctly. (e.g. {@link isPipShown}). */ void onPipEntered(); /** Invoked when a PIPed activity is closed. */ @@ -632,6 +639,12 @@ public class PipManager { void onMoveToFullscreen(); /** Invoked when we are above to start resizing the Pip. */ void onPipResizeAboutToStart(); + } + + /** + * A listener interface to receive change in PIP's media controller + */ + public interface MediaListener { /** Invoked when the MediaController on PIPed activity is changed. */ void onMediaControllerChanged(); } @@ -645,4 +658,11 @@ public class PipManager { } return sPipManager; } + + /** + * Gets an instance of {@link PipRecentsOverlayManager}. + */ + public PipRecentsOverlayManager getPipRecentsOverlayManager() { + return mPipRecentsOverlayManager; + } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java index ea9275f8a918..c54e73a7cda6 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java @@ -20,12 +20,6 @@ import android.app.Activity; import android.os.Bundle; import com.android.systemui.R; -import com.android.systemui.SystemUI; -import com.android.systemui.SystemUIApplication; -import com.android.systemui.recents.Recents; - -import static android.content.pm.PackageManager.FEATURE_LEANBACK; -import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; /** * Activity to show the PIP menu to control PIP. @@ -36,7 +30,6 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { private final PipManager mPipManager = PipManager.getInstance(); private PipControlsView mPipControlsView; - private boolean mPipMovedToFullscreen; @Override protected void onCreate(Bundle bundle) { @@ -47,17 +40,10 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls); } - private void restorePipAndFinish() { - if (!mPipMovedToFullscreen) { - mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY); - } - finish(); - } - @Override public void onPause() { super.onPause(); - restorePipAndFinish(); + finish(); } @Override @@ -69,11 +55,6 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { } @Override - public void onBackPressed() { - restorePipAndFinish(); - } - - @Override public void onPipEntered() { } @Override @@ -86,31 +67,13 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void onMoveToFullscreen() { - mPipMovedToFullscreen = true; finish(); } @Override - public void onMediaControllerChanged() { } - - @Override public void onPipResizeAboutToStart() { finish(); mPipManager.suspendPipResizing( PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); } - - @Override - public void finish() { - super.finish(); - if (mPipManager.isRecentsShown() && !mPipMovedToFullscreen) { - SystemUI[] services = ((SystemUIApplication) getApplication()).getServices(); - for (int i = services.length - 1; i >= 0; i--) { - if (services[i] instanceof Recents) { - ((Recents) services[i]).showRecents(false, null); - break; - } - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java index 79daf3d51056..86ceff48408f 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java @@ -86,7 +86,4 @@ public class PipOnboardingActivity extends Activity implements PipManager.Listen @Override public void onPipResizeAboutToStart() { } - - @Override - public void onMediaControllerChanged() { } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java index 12cb4cd77824..5472ad6718dd 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java @@ -35,14 +35,6 @@ import static android.app.ActivityManager.StackId.PINNED_STACK_ID; public class PipOverlayActivity extends Activity implements PipManager.Listener { private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000; - /** - * The single instance of PipOverlayActivity to prevent it from restarting. - * Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is - * restarted. It's because the activity may be started by the Launcher or an intent again, - * but we don't want do so for the PipOverlayActivity. - */ - private static PipOverlayActivity sPipOverlayActivity; - private final PipManager mPipManager = PipManager.getInstance(); private final Handler mHandler = new Handler(); private View mGuideOverlayView; @@ -54,47 +46,17 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener } }; - /** - * Launches the PIP overlay. This should be only called on the main thread. - */ - public static void showPipOverlay(Context context) { - if (sPipOverlayActivity == null) { - Intent intent = new Intent(context, PipOverlayActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchStackId(PINNED_STACK_ID); - context.startActivity(intent, options.toBundle()); - } - } - @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.tv_pip_overlay); mGuideOverlayView = findViewById(R.id.guide_overlay); - mGuideButtonsView = findViewById(R.id.guide_buttons); - mGuideButtonPlayPauseImageView = (ImageView) findViewById(R.id.guide_button_play_pause); mPipManager.addListener(this); - - sPipOverlayActivity = this; } @Override protected void onResume() { super.onResume(); - // TODO: Implement animation for this - if (mPipManager.isRecentsShown()) { - mGuideOverlayView.setVisibility(View.GONE); - if (mPipManager.isPipViewFocusdInRecents()) { - mGuideButtonsView.setVisibility(View.GONE); - } else { - mGuideButtonsView.setVisibility(View.VISIBLE); - updateGuideButtonsView(); - } - } else { - mGuideOverlayView.setVisibility(View.VISIBLE); - mGuideButtonsView.setVisibility(View.GONE); - } mHandler.removeCallbacks(mHideGuideOverlayRunnable); mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS); } @@ -109,7 +71,6 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener @Override protected void onDestroy() { super.onDestroy(); - sPipOverlayActivity = null; mHandler.removeCallbacksAndMessages(null); mPipManager.removeListener(this); mPipManager.resumePipResizing( @@ -140,32 +101,4 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener mPipManager.suspendPipResizing( PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH); } - - @Override - public void onMediaControllerChanged() { - updateGuideButtonsView(); - } - - @Override - public void finish() { - sPipOverlayActivity = null; - super.finish(); - } - - private void updateGuideButtonsView() { - switch (mPipManager.getPlaybackState()) { - case PipManager.PLAYBACK_STATE_PLAYING: - mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE); - mGuideButtonPlayPauseImageView.setImageResource(R.drawable.ic_pause_white_24dp); - break; - case PipManager.PLAYBACK_STATE_PAUSED: - mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE); - mGuideButtonPlayPauseImageView.setImageResource( - R.drawable.ic_play_arrow_white_24dp); - break; - case PipManager.PLAYBACK_STATE_UNAVAILABLE: - mGuideButtonPlayPauseImageView.setVisibility(View.GONE); - break; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java new file mode 100644 index 000000000000..8b8c1058bb99 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.tv.pip; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnFocusChangeListener; + +import com.android.systemui.R; + +import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING; +import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED; +import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE; + +/** + * An extended version of {@link PipControlsView} that supports animation in Recents. + */ +public class PipRecentsControlsView extends PipControlsView { + /** + * An interface to listen user action. + */ + public interface Listener extends PipControlsView.Listener { + /** + * Called when an user presses BACK key and up. + */ + abstract void onBackPressed(); + } + + private AnimatorSet mFocusGainAnimatorSet; + private AnimatorSet mFocusLoseAnimatorSet; + + public PipRecentsControlsView(Context context) { + this(context, null, 0, 0); + } + + public PipRecentsControlsView(Context context, AttributeSet attrs) { + this(context, attrs, 0, 0); + } + + public PipRecentsControlsView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public PipRecentsControlsView( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + + int buttonsFocusGainAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_gain_animation; + int textFocusGainAnim = R.anim.tv_pip_controls_text_in_recents_focus_gain_animation; + mFocusGainAnimatorSet = new AnimatorSet(); + mFocusGainAnimatorSet.playTogether( + loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_gain_animation), + loadAnimator(mFullButtonView,buttonsFocusGainAnim), + loadAnimator(mPlayPauseButtonImageView, buttonsFocusGainAnim), + loadAnimator(mCloseButtonView, buttonsFocusGainAnim), + loadAnimator(mFullDescriptionView, textFocusGainAnim), + loadAnimator(mPlayPauseDescriptionTextView, textFocusGainAnim), + loadAnimator(mCloseDescriptionView, textFocusGainAnim)); + + int buttonsFocusLoseAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_lose_animation; + int textFocusLoseAnim = R.anim.tv_pip_controls_text_in_recents_focus_lose_animation; + mFocusLoseAnimatorSet = new AnimatorSet(); + mFocusLoseAnimatorSet.playTogether( + loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_lose_animation), + loadAnimator(mFullButtonView, buttonsFocusLoseAnim), + loadAnimator(mPlayPauseButtonImageView, buttonsFocusLoseAnim), + loadAnimator(mCloseButtonView, buttonsFocusLoseAnim), + loadAnimator(mFullDescriptionView, textFocusLoseAnim), + loadAnimator(mPlayPauseDescriptionTextView, textFocusLoseAnim), + loadAnimator(mCloseDescriptionView, textFocusLoseAnim)); + + Rect pipBounds = mPipManager.getRecentsFocusedPipBounds(); + int pipControlsMarginTop = getContext().getResources().getDimensionPixelSize( + R.dimen.recents_tv_pip_controls_margin_top); + setPadding(0, pipBounds.bottom + pipControlsMarginTop, 0, 0); + } + + private Animator loadAnimator(View view, int animatorResId) { + Animator animator = AnimatorInflater.loadAnimator(getContext(), animatorResId); + animator.setTarget(view); + return animator; + } + + /** + * Starts focus gaining animation. + */ + public void startFocusGainAnimation() { + if (mFocusLoseAnimatorSet.isStarted()) { + mFocusLoseAnimatorSet.cancel(); + } + mFocusGainAnimatorSet.start(); + } + + /** + * Starts focus losing animation. + */ + public void startFocusLoseAnimation() { + if (mFocusGainAnimatorSet.isStarted()) { + mFocusGainAnimatorSet.cancel(); + } + mFocusLoseAnimatorSet.start(); + } + + /** + * Resets the view to the initial state. (i.e. end of the focus gain) + */ + public void reset() { + if (mFocusGainAnimatorSet.isStarted()) { + mFocusGainAnimatorSet.cancel(); + } + if (mFocusLoseAnimatorSet.isStarted()) { + mFocusLoseAnimatorSet.cancel(); + } + + // Reset to initial state (i.e. end of focused) + requestFocus(); + setTranslationY(0); + setScaleXY(mFullButtonView, 1); + setScaleXY(mPlayPauseButtonImageView, 1); + setScaleXY(mCloseButtonView, 1); + mFullDescriptionView.setAlpha(1); + mPlayPauseDescriptionTextView.setAlpha(1); + mCloseDescriptionView.setAlpha(1); + } + + private void setScaleXY(View view, float scale) { + view.setScaleX(scale); + view.setScaleY(scale); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (!event.isCanceled() + && event.getKeyCode() == KeyEvent.KEYCODE_BACK + && event.getAction() == KeyEvent.ACTION_UP) { + if (mListener != null) { + ((PipRecentsControlsView.Listener) mListener).onBackPressed(); + } + return true; + } + return super.dispatchKeyEvent(event); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java new file mode 100644 index 000000000000..b90b7279a5b5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.tv.pip; + +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.content.Context; +import android.graphics.PixelFormat; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManager; + +import com.android.systemui.R; + +import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY; +import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS; +import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED; + +public class PipRecentsOverlayManager { + private static final String TAG = "PipRecentsOverlayManager"; + + public interface Callback { + void onClosed(); + void onBackPressed(); + void onRecentsFocused(); + } + + private final PipManager mPipManager = PipManager.getInstance(); + private final WindowManager mWindowManager; + private final View mOverlayView; + private final PipRecentsControlsView mPipControlsView; + private final View mRecentsView; + + private final LayoutParams mPipRecentsControlsViewLayoutParams; + private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams; + + private boolean mIsPipRecentsOverlayShown; + private boolean mIsRecentsShown; + private boolean mIsPipFocusedInRecent; + private Callback mCallback; + private PipRecentsControlsView.Listener mPipControlsViewListener = + new PipRecentsControlsView.Listener() { + @Override + public void onClosed() { + if (mCallback != null) { + mCallback.onClosed(); + } + } + + @Override + public void onBackPressed() { + if (mCallback != null) { + mCallback.onBackPressed(); + } + } + }; + + PipRecentsOverlayManager(Context context) { + mWindowManager = (WindowManager) context.getSystemService(WindowManager.class); + + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null); + mPipControlsView = (PipRecentsControlsView) mOverlayView.findViewById(R.id.pip_controls); + mRecentsView = mOverlayView.findViewById(R.id.recents); + mRecentsView.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + clearFocus(); + } + } + }); + + mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + LayoutParams.TYPE_SYSTEM_DIALOG, + LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + LayoutParams.TYPE_SYSTEM_DIALOG, + 0, + PixelFormat.TRANSLUCENT); + } + + /** + * Add Recents overlay view. + * This is expected to be called after the PIP animation is over. + */ + void addPipRecentsOverlayView() { + if (mIsPipRecentsOverlayShown) { + return; + } + mIsPipRecentsOverlayShown = true; + mIsPipFocusedInRecent = true; + mPipControlsView.reset(); + mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams); + } + + /** + * Remove Recents overlay view. + * This should be called when Recents or PIP is closed. + */ + public void removePipRecentsOverlayView() { + if (!mIsPipRecentsOverlayShown) { + return; + } + mWindowManager.removeView(mOverlayView); + mIsPipRecentsOverlayShown = false; + } + + /** + * Request focus to the PIP Recents overlay. + * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity} + * is focused. + * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}. + * @param hasRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task) + */ + public void requestFocus(boolean hasRecentsFocusable) { + if (!mIsRecentsShown || mIsPipFocusedInRecent) { + return; + } + mIsPipFocusedInRecent = true; + mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED); + + mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams); + mPipControlsView.requestFocus(); + mPipControlsView.startFocusGainAnimation(); + mRecentsView.setVisibility(hasRecentsFocusable ? View.VISIBLE : View.GONE); + } + + /** + * Request focus to the PIP Recents overlay. + * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity} + * is focused. + * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}. + */ + private void clearFocus() { + if (!mIsRecentsShown || !mIsPipFocusedInRecent) { + return; + } + mIsPipFocusedInRecent = false; + mPipManager.resizePinnedStack(STATE_PIP_RECENTS); + mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams); + mPipControlsView.startFocusLoseAnimation(); + if (mCallback != null) { + mCallback.onRecentsFocused(); + } + } + + public void setCallback(Callback listener) { + mCallback = listener; + mPipControlsView.setListener(mCallback != null ? mPipControlsViewListener : null); + } + + /** + * Called when Recents is resumed. + * PIPed activity will be resized accordingly and overlay will show available buttons. + */ + public void onRecentsResumed() { + if (!mPipManager.isPipShown()) { + return; + } + mIsRecentsShown = true; + mIsPipFocusedInRecent = true; + mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED); + // Overlay view will be added after the resize animation ends, if any. + } + + /** + * Called when Recents is paused. + * PIPed activity will be resized accordingly and overlay will hide available buttons. + */ + public void onRecentsPaused() { + mIsRecentsShown = false; + mIsPipFocusedInRecent = false; + removePipRecentsOverlayView(); + + if (mPipManager.isPipShown()) { + mPipManager.resizePinnedStack(STATE_PIP_OVERLAY); + } + } + + /** + * Returns {@code true} if recents is shown. + */ + boolean isRecentsShown() { + return mIsRecentsShown; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java index f4328089bf0f..c6e4356aa420 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java +++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java @@ -35,11 +35,11 @@ public class SegmentedButtons extends LinearLayout { private static final Typeface MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL); private final Context mContext; - private final LayoutInflater mInflater; + protected final LayoutInflater mInflater; private final SpTexts mSpTexts; private Callback mCallback; - private Object mSelectedValue; + protected Object mSelectedValue; public SegmentedButtons(Context context, AttributeSet attrs) { super(context, attrs); @@ -65,13 +65,21 @@ public class SegmentedButtons extends LinearLayout { final Object tag = c.getTag(); final boolean selected = Objects.equals(mSelectedValue, tag); c.setSelected(selected); - c.setTypeface(selected ? MEDIUM : REGULAR); + setSelectedStyle(c, selected); } fireOnSelected(fromClick); } + protected void setSelectedStyle(TextView textView, boolean selected) { + textView.setTypeface(selected ? MEDIUM : REGULAR); + } + + public Button inflateButton() { + return (Button) mInflater.inflate(R.layout.segmented_button, this, false); + } + public void addButton(int labelResId, int contentDescriptionResId, Object value) { - final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false); + final Button b = inflateButton(); b.setTag(LABEL_RES_KEY, labelResId); b.setText(labelResId); b.setContentDescription(getResources().getString(contentDescriptionResId)); diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml index 32e1e6de687f..08257b63a7ff 100644 --- a/packages/VpnDialogs/AndroidManifest.xml +++ b/packages/VpnDialogs/AndroidManifest.xml @@ -24,7 +24,7 @@ <application android:label="VpnDialogs" android:allowBackup="false" > <activity android:name=".ConfirmDialog" - android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert"> + android:theme="@android:style/Theme.Material.Light.Dialog.Alert"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT"/> @@ -32,7 +32,7 @@ </activity> <activity android:name=".ManageDialog" - android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert" + android:theme="@android:style/Theme.Material.Light.Dialog.Alert" android:noHistory="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/packages/WallpaperCropper/res/values/styles.xml b/packages/WallpaperCropper/res/values/styles.xml index 0f9e247f1c2b..6a56afd9de50 100644 --- a/packages/WallpaperCropper/res/values/styles.xml +++ b/packages/WallpaperCropper/res/values/styles.xml @@ -15,7 +15,7 @@ --> <resources> - <style name="Theme.WallpaperCropper" parent="@*android:style/Theme.Material.DayNight"> + <style name="Theme.WallpaperCropper" parent="@android:style/Theme.Material.Light"> <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item> <item name="android:windowFullscreen">true</item> <item name="android:windowActionBarOverlay">true</item> diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java index 81f63aeab0a5..9ec6e8daf429 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -380,6 +380,7 @@ public class Allocation extends BaseObj { Log.e(RenderScript.LOG_TAG, "Couldn't invoke registerNativeAllocation:" + e); throw new RSRuntimeException("Couldn't invoke registerNativeAllocation:" + e); } + guard.open("destroy"); } Allocation(long id, RenderScript rs, Type t, int usage, MipmapControl mips) { @@ -1915,6 +1916,7 @@ public class Allocation extends BaseObj { if (type.getID(rs) == 0) { throw new RSInvalidStateException("Bad Type"); } + // TODO: What if there is an exception after this? The native allocation would leak. long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0); if (id == 0) { throw new RSRuntimeException("Allocation creation failed."); diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java index 1372ab79e264..f95af1673730 100644 --- a/rs/java/android/renderscript/BaseObj.java +++ b/rs/java/android/renderscript/BaseObj.java @@ -16,6 +16,7 @@ package android.renderscript; +import dalvik.system.CloseGuard; import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -69,6 +70,7 @@ public class BaseObj { } private long mID; + final CloseGuard guard = CloseGuard.get(); private boolean mDestroyed; private String mName; RenderScript mRS; @@ -119,6 +121,7 @@ public class BaseObj { } if (shouldDestroy) { + guard.close(); // must include nObjDestroy in the critical section ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock(); rlock.lock(); @@ -133,8 +136,14 @@ public class BaseObj { } protected void finalize() throws Throwable { - helpDestroy(); - super.finalize(); + try { + if (guard != null) { + guard.warnIfOpen(); + } + helpDestroy(); + } finally { + super.finalize(); + } } /** diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java index 6efb6d6a5735..50226acc77b4 100644 --- a/rs/java/android/renderscript/Element.java +++ b/rs/java/android/renderscript/Element.java @@ -808,6 +808,7 @@ public class Element extends BaseObj { mSize += mElements[ct].mSize * mArraySizes[ct]; } updateVisibleSubElements(); + guard.open("destroy"); } Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) { @@ -827,6 +828,7 @@ public class Element extends BaseObj { mKind = dk; mNormalized = norm; mVectorSize = size; + guard.open("destroy"); } Element(long id, RenderScript rs) { diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java index 9d8f1624a051..278d309c74f7 100644 --- a/rs/java/android/renderscript/FileA3D.java +++ b/rs/java/android/renderscript/FileA3D.java @@ -170,6 +170,7 @@ public class FileA3D extends BaseObj { FileA3D(long id, RenderScript rs, InputStream stream) { super(id, rs); mInputStream = stream; + guard.open("destroy"); } private void initEntries() { diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java index 4318b9d4d21c..d5ca31e93418 100644 --- a/rs/java/android/renderscript/Font.java +++ b/rs/java/android/renderscript/Font.java @@ -150,6 +150,7 @@ public class Font extends BaseObj { Font(long id, RenderScript rs) { super(id, rs); + guard.open("destroy"); } /** diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java index 13c8e1c91052..9e4f90573ae9 100644 --- a/rs/java/android/renderscript/Mesh.java +++ b/rs/java/android/renderscript/Mesh.java @@ -91,6 +91,7 @@ public class Mesh extends BaseObj { Mesh(long id, RenderScript rs) { super(id, rs); + guard.open("destroy"); } /** diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java index 3eb9b7590f45..772021c7815c 100644 --- a/rs/java/android/renderscript/Program.java +++ b/rs/java/android/renderscript/Program.java @@ -76,6 +76,7 @@ public class Program extends BaseObj { Program(long id, RenderScript rs) { super(id, rs); + guard.open("destroy"); } /** diff --git a/rs/java/android/renderscript/Sampler.java b/rs/java/android/renderscript/Sampler.java index a4edbb50ac10..5c4bae99ba8f 100644 --- a/rs/java/android/renderscript/Sampler.java +++ b/rs/java/android/renderscript/Sampler.java @@ -51,6 +51,7 @@ public class Sampler extends BaseObj { Sampler(long id, RenderScript rs) { super(id, rs); + guard.open("destroy"); } /** diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java index f6f93cb7b964..fc3280be3ac7 100644 --- a/rs/java/android/renderscript/Script.java +++ b/rs/java/android/renderscript/Script.java @@ -41,6 +41,7 @@ public class Script extends BaseObj { mScript = s; mSlot = slot; mSig = sig; + guard.open("destroy"); } } @@ -118,6 +119,7 @@ public class Script extends BaseObj { super(id, rs); mScript = s; mSlot = slot; + guard.open("destroy"); } } @@ -357,6 +359,19 @@ public class Script extends BaseObj { super(id, rs); mInIdsBuffer = new long[1]; + + /* The constructors for the derived classes (including ScriptIntrinsic + * derived classes and ScriptC derived classes generated by Slang + * reflection) seem to be simple enough, so we just put the guard.open() + * call here, rather than in the end of the constructor for the derived + * class. This, of course, assumes the derived constructor would not + * throw any exception after calling this constructor. + * + * If new derived classes are added with more complicated constructors + * that throw exceptions, this call has to be (duplicated and) moved + * to the end of each derived class constructor. + */ + guard.open("destroy"); } /** diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java index 9357c3bb0428..219f16b91baf 100644 --- a/rs/java/android/renderscript/ScriptGroup.java +++ b/rs/java/android/renderscript/ScriptGroup.java @@ -148,6 +148,8 @@ public final class ScriptGroup extends BaseObj { fieldIDs, values, sizes, depClosures, depFieldIDs); setID(id); + + guard.open("destroy"); } Closure(RenderScript rs, Script.InvokeID invokeID, @@ -181,6 +183,8 @@ public final class ScriptGroup extends BaseObj { values, sizes); setID(id); + + guard.open("destroy"); } private void retrieveValueAndDependenceInfo(RenderScript rs, @@ -382,6 +386,7 @@ public final class ScriptGroup extends BaseObj { ScriptGroup(long id, RenderScript rs) { super(id, rs); + guard.open("destroy"); } ScriptGroup(RenderScript rs, String name, List<Closure> closures, @@ -398,6 +403,7 @@ public final class ScriptGroup extends BaseObj { } long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs); setID(id); + guard.open("destroy"); } /** diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java index dc2378596d00..9252898781f4 100644 --- a/rs/java/android/renderscript/Type.java +++ b/rs/java/android/renderscript/Type.java @@ -227,6 +227,7 @@ public class Type extends BaseObj { Type(long id, RenderScript rs) { super(id, rs); + guard.open("destroy"); } @Override diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index fd57af6b02f0..c38a89e30807 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -446,8 +446,12 @@ public class AppOpsService extends IAppOpsService.Stub { int[] ops) { mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null); + String resolvedPackageName = resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return Collections.emptyList(); + } synchronized (this) { - Ops pkgOps = getOpsLocked(uid, packageName, false); + Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false); if (pkgOps == null) { return null; } @@ -465,7 +469,7 @@ public class AppOpsService extends IAppOpsService.Stub { private void pruneOp(Op op, int uid, String packageName) { if (op.time == 0 && op.rejectTime == 0) { - Ops ops = getOpsLocked(uid, packageName, false); + Ops ops = getOpsRawLocked(uid, packageName, false); if (ops != null) { ops.remove(op.op); if (ops.size() <= 0) { @@ -879,8 +883,12 @@ public class AppOpsService extends IAppOpsService.Stub { public int checkOperation(int code, int uid, String packageName) { verifyIncomingUid(uid); verifyIncomingOp(code); + String resolvedPackageName = resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } synchronized (this) { - if (isOpRestricted(uid, code, packageName)) { + if (isOpRestricted(uid, code, resolvedPackageName)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -891,7 +899,7 @@ public class AppOpsService extends IAppOpsService.Stub { return uidMode; } } - Op op = getOpLocked(code, uid, packageName, false); + Op op = getOpLocked(code, uid, resolvedPackageName, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } @@ -975,6 +983,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkPackage(int uid, String packageName) { + Preconditions.checkNotNull(packageName); synchronized (this) { if (getOpsRawLocked(uid, packageName, true) != null) { return AppOpsManager.MODE_ALLOWED; @@ -988,26 +997,39 @@ public class AppOpsService extends IAppOpsService.Stub { public int noteProxyOperation(int code, String proxyPackageName, int proxiedUid, String proxiedPackageName) { verifyIncomingOp(code); - final int proxyMode = noteOperationUnchecked(code, Binder.getCallingUid(), - proxyPackageName, -1, null); + final int proxyUid = Binder.getCallingUid(); + String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName); + if (resolveProxyPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } + final int proxyMode = noteOperationUnchecked(code, proxyUid, + resolveProxyPackageName, -1, null); if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) { return proxyMode; } - return noteOperationUnchecked(code, proxiedUid, proxiedPackageName, - Binder.getCallingUid(), proxyPackageName); + String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName); + if (resolveProxiedPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } + return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName, + proxyMode, resolveProxyPackageName); } @Override public int noteOperation(int code, int uid, String packageName) { verifyIncomingUid(uid); verifyIncomingOp(code); - return noteOperationUnchecked(code, uid, packageName, 0, null); + String resolvedPackageName = resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } + return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null); } private int noteOperationUnchecked(int code, int uid, String packageName, int proxyUid, String proxyPackageName) { synchronized (this) { - Ops ops = getOpsLocked(uid, packageName, true); + Ops ops = getOpsRawLocked(uid, packageName, true); if (ops == null) { if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); @@ -1055,16 +1077,20 @@ public class AppOpsService extends IAppOpsService.Stub { public int startOperation(IBinder token, int code, int uid, String packageName) { verifyIncomingUid(uid); verifyIncomingOp(code); + String resolvedPackageName = resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } ClientState client = (ClientState)token; synchronized (this) { - Ops ops = getOpsLocked(uid, packageName, true); + Ops ops = getOpsRawLocked(uid, resolvedPackageName, true); if (ops == null) { if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid - + " package " + packageName); + + " package " + resolvedPackageName); return AppOpsManager.MODE_ERRORED; } Op op = getOpLocked(ops, code, true); - if (isOpRestricted(uid, code, packageName)) { + if (isOpRestricted(uid, code, resolvedPackageName)) { return AppOpsManager.MODE_IGNORED; } final int switchCode = AppOpsManager.opToSwitch(code); @@ -1074,7 +1100,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidMode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " - + packageName); + + resolvedPackageName); op.rejectTime = System.currentTimeMillis(); return uidMode; } @@ -1082,12 +1108,13 @@ public class AppOpsService extends IAppOpsService.Stub { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code " - + switchCode + " (" + code + ") uid " + uid + " package " + packageName); + + switchCode + " (" + code + ") uid " + uid + " package " + + resolvedPackageName); op.rejectTime = System.currentTimeMillis(); return switchOp.mode; } if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid - + " package " + packageName); + + " package " + resolvedPackageName); if (op.nesting == 0) { op.time = System.currentTimeMillis(); op.rejectTime = 0; @@ -1105,9 +1132,16 @@ public class AppOpsService extends IAppOpsService.Stub { public void finishOperation(IBinder token, int code, int uid, String packageName) { verifyIncomingUid(uid); verifyIncomingOp(code); - ClientState client = (ClientState)token; + String resolvedPackageName = resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return; + } + if (!(token instanceof ClientState)) { + return; + } + ClientState client = (ClientState) token; synchronized (this) { - Op op = getOpLocked(code, uid, packageName, true); + Op op = getOpLocked(code, uid, resolvedPackageName, true); if (op == null) { return; } @@ -1123,6 +1157,9 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int permissionToOpCode(String permission) { + if (permission == null) { + return AppOpsManager.OP_NONE; + } return AppOpsManager.permissionToOpCode(permission); } @@ -1172,15 +1209,6 @@ public class AppOpsService extends IAppOpsService.Stub { return uidState; } - private Ops getOpsLocked(int uid, String packageName, boolean edit) { - if (uid == 0) { - packageName = "root"; - } else if (uid == Process.SHELL_UID) { - packageName = "com.android.shell"; - } - return getOpsRawLocked(uid, packageName, edit); - } - private Ops getOpsRawLocked(int uid, String packageName, boolean edit) { UidState uidState = getUidStateLocked(uid, edit); if (uidState == null) { @@ -1266,7 +1294,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private Op getOpLocked(int code, int uid, String packageName, boolean edit) { - Ops ops = getOpsLocked(uid, packageName, edit); + Ops ops = getOpsRawLocked(uid, packageName, edit); if (ops == null) { return null; } @@ -1324,7 +1352,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (AppOpsManager.opAllowSystemBypassRestriction(code)) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { - Ops ops = getOpsLocked(uid, packageName, true); + Ops ops = getOpsRawLocked(uid, packageName, true); if ((ops != null) && ops.isPrivileged) { return false; } @@ -1589,7 +1617,7 @@ public class AppOpsService extends IAppOpsService.Stub { out.startTag(null, "uid"); out.attribute(null, "n", Integer.toString(pkg.getUid())); synchronized (this) { - Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false); + Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false); // Should always be present as the list of PackageOps is generated // from Ops. if (ops != null) { @@ -2181,6 +2209,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) { checkSystemUid("setUserRestrictions"); + Preconditions.checkNotNull(restrictions); Preconditions.checkNotNull(token); final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); for (int i = 0; i < opRestrictions.length; ++i) { @@ -2395,6 +2424,17 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private static String resolvePackageName(int uid, String packageName) { + if (uid == 0) { + return "root"; + } else if (uid == Process.SHELL_UID) { + return "com.android.shell"; + } else if (uid == Process.SYSTEM_UID && packageName == null) { + return "android"; + } + return packageName; + } + private static String[] getPackagesForUid(int uid) { String[] packageNames = null; try { diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 799a763845d2..0a814ab579d3 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -92,6 +92,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60; private static final int MESSAGE_TIMEOUT_BIND = 100; private static final int MESSAGE_TIMEOUT_UNBIND = 101; + private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200; private static final int MESSAGE_USER_SWITCHED = 300; private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; @@ -599,8 +600,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(true); } return true; - } + public boolean enable() { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { @@ -763,9 +764,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(mQuietEnableExternal); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); - enable(); - waitForOnOff(true, false); - disable(true); + Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(getMsg); } } @@ -1076,6 +1076,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private BluetoothServiceConnection mConnection = new BluetoothServiceConnection(); private class BluetoothHandler extends Handler { + boolean mGetNameAddressOnly = false; + public BluetoothHandler(Looper looper) { super(looper); } @@ -1084,6 +1086,37 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void handleMessage(Message msg) { if (DBG) Slog.d (TAG, "Message: " + msg.what); switch (msg.what) { + case MESSAGE_GET_NAME_AND_ADDRESS: + if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); + synchronized(mConnection) { + if ((mBluetooth == null) && (!mBinding)) { + if (DBG) Slog.d(TAG, "Binding to service to get name and address"); + mGetNameAddressOnly = true; + Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); + mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); + Intent i = new Intent(IBluetooth.class.getName()); + if (!doBind(i, mConnection, + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT)) { + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + } else { + mBinding = true; + } + } else if (mBluetooth != null) { + try { + storeNameAndAddress(mBluetooth.getName(), + mBluetooth.getAddress()); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to grab names", re); + } + if (mGetNameAddressOnly && !mEnable) { + unbindAndFinish(); + } + mGetNameAddressOnly = false; + } + } + break; + case MESSAGE_ENABLE: if (DBG) { Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth); @@ -1177,6 +1210,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothBinder = service; mBluetooth = IBluetooth.Stub.asInterface(service); + if (!isNameAndAddressSet()) { + Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(getMsg); + if (mGetNameAddressOnly) return; + } + try { boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver, Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1); @@ -1187,15 +1226,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG,"Unable to call configHciSnoopLog", e); } - if (!isNameAndAddressSet()) { - try { - storeNameAndAddress(mBluetooth.getName(), - mBluetooth.getAddress()); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to grab names", re); - } - } - //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index bb323031c011..8a0a62a033ed 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -782,7 +782,12 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); - return readUserDataInternal(accounts, account, key); + synchronized (accounts.cacheLock) { + if (!accountExistsCacheLocked(accounts, account)) { + return null; + } + return readUserDataInternalLocked(accounts, account, key); + } } finally { restoreCallingIdentity(identityToken); } @@ -1869,44 +1874,57 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); - setUserdataInternal(accounts, account, key, value); + synchronized (accounts.cacheLock) { + if (!accountExistsCacheLocked(accounts, account)) { + return; + } + setUserdataInternalLocked(accounts, account, key, value); + } } finally { restoreCallingIdentity(identityToken); } } - private void setUserdataInternal(UserAccounts accounts, Account account, String key, + private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) { + if (accounts.accountCache.containsKey(account.type)) { + for (Account acc : accounts.accountCache.get(account.type)) { + if (acc.name.equals(account.name)) { + return true; + } + } + } + return false; + } + + private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key, String value) { if (account == null || key == null) { return; } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId < 0) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountIdLocked(db, account); + if (accountId < 0) { + return; + } + long extrasId = getExtrasIdLocked(db, accountId, key); + if (extrasId < 0) { + extrasId = insertExtraLocked(db, accountId, key, value); + if (extrasId < 0) { return; } - long extrasId = getExtrasIdLocked(db, accountId, key); - if (extrasId < 0 ) { - extrasId = insertExtraLocked(db, accountId, key, value); - if (extrasId < 0) { - return; - } - } else { - ContentValues values = new ContentValues(); - values.put(EXTRAS_VALUE, value); - if (1 != db.update(CE_TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { - return; - } - + } else { + ContentValues values = new ContentValues(); + values.put(EXTRAS_VALUE, value); + if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { + return; } - writeUserDataIntoCacheLocked(accounts, db, account, key, value); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); } + writeUserDataIntoCacheLocked(accounts, db, account, key, value); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } } @@ -2500,21 +2518,31 @@ public class AccountManagerService userId); return; } - final int pid = Binder.getCallingPid(); final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; options.putInt(AccountManager.KEY_CALLER_UID, uid); options.putInt(AccountManager.KEY_CALLER_PID, pid); + // Check to see if the Password should be included to the caller. + String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); + boolean isPasswordForwardingAllowed = isPermitted( + callerPkg, uid, Manifest.permission.GET_PASSWORD_PRIVILEGED); + int usrId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(usrId); logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD, TABLE_ACCOUNTS, uid); - new StartAccountSession(accounts, response, accountType, expectActivityLaunch, - null /* accountName */, false /* authDetailsRequired */, - true /* updateLastAuthenticationTime */) { + new StartAccountSession( + accounts, + response, + accountType, + expectActivityLaunch, + null /* accountName */, + false /* authDetailsRequired */, + true /* updateLastAuthenticationTime */, + isPasswordForwardingAllowed) { @Override public void run() throws RemoteException { mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType, @@ -2537,12 +2565,21 @@ public class AccountManagerService /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */ private abstract class StartAccountSession extends Session { - public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response, - String accountType, boolean expectActivityLaunch, String accountName, - boolean authDetailsRequired, boolean updateLastAuthenticationTime) { + private final boolean mIsPasswordForwardingAllowed; + + public StartAccountSession( + UserAccounts accounts, + IAccountManagerResponse response, + String accountType, + boolean expectActivityLaunch, + String accountName, + boolean authDetailsRequired, + boolean updateLastAuthenticationTime, + boolean isPasswordForwardingAllowed) { super(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */, accountName, authDetailsRequired, updateLastAuthenticationTime); + mIsPasswordForwardingAllowed = isPasswordForwardingAllowed; } @Override @@ -2555,6 +2592,10 @@ public class AccountManagerService checkKeyIntent( Binder.getCallingUid(), intent); + // Omit passwords if the caller isn't permitted to see them. + if (!mIsPasswordForwardingAllowed) { + result.remove(AccountManager.KEY_PASSWORD); + } } IAccountManagerResponse response; if (mExpectActivityLaunch && result != null @@ -2901,6 +2942,12 @@ public class AccountManagerService } int userId = UserHandle.getCallingUserId(); + + // Check to see if the Password should be included to the caller. + String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); + boolean isPasswordForwardingAllowed = isPermitted( + callerPkg, uid, Manifest.permission.GET_PASSWORD_PRIVILEGED); + long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -2911,7 +2958,8 @@ public class AccountManagerService expectActivityLaunch, account.name, false /* authDetailsRequired */, - true /* updateLastCredentialTime */) { + true /* updateLastCredentialTime */, + isPasswordForwardingAllowed) { @Override public void run() throws RemoteException { mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType, @@ -5262,17 +5310,16 @@ public class AccountManagerService } } - protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { - synchronized (accounts.cacheLock) { - HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); - if (userDataForAccount == null) { - // need to populate the cache for this account - final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked(); - userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); - accounts.userDataCache.put(account, userDataForAccount); - } - return userDataForAccount.get(key); + protected String readUserDataInternalLocked( + UserAccounts accounts, Account account, String key) { + HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); + if (userDataForAccount == null) { + // need to populate the cache for this account + final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); + userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); + accounts.userDataCache.put(account, userDataForAccount); } + return userDataForAccount.get(key); } protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a95c8747eebb..1da6a2095f2f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9827,7 +9827,8 @@ public final class ActivityManagerService extends ActivityManagerNative public void updateLockTaskPackages(int userId, String[] packages) { final int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { - throw new SecurityException("updateLockTaskPackage called from non-system process"); + enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES, + "updateLockTaskPackages()"); } synchronized (this) { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + @@ -18044,6 +18045,9 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityRecord starting, boolean initLocale, boolean persistent, int userId) { int changes = 0; + if (mWindowManager != null) { + mWindowManager.deferSurfaceLayout(); + } if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); @@ -18144,6 +18148,20 @@ public final class ActivityManagerService extends ActivityManagerNative null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } } + // Update the configuration with WM first and check if any of the stacks need to be + // resized due to the configuration change. If so, resize the stacks now and do any + // relaunches if necessary. This way we don't need to relaunch again below in + // ensureActivityConfigurationLocked(). + if (mWindowManager != null) { + final int[] resizedStacks = mWindowManager.setNewConfiguration(mConfiguration); + if (resizedStacks != null) { + for (int stackId : resizedStacks) { + final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId); + mStackSupervisor.resizeStackLocked( + stackId, newBounds, null, null, false, false); + } + } + } } boolean kept = true; @@ -18165,11 +18183,9 @@ public final class ActivityManagerService extends ActivityManagerNative !PRESERVE_WINDOWS); } } - - if (values != null && mWindowManager != null) { - mWindowManager.setNewConfiguration(mConfiguration); + if (mWindowManager != null) { + mWindowManager.continueSurfaceLayout(); } - return kept; } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index d570be94a8a4..9be6b43f21c6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -23,8 +23,6 @@ import android.os.ShellCommand; import android.os.UserHandle; import android.util.DebugUtils; -import com.android.internal.util.ArrayUtils; - import java.io.PrintWriter; class ActivityManagerShellCommand extends ShellCommand { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index b297938b8c86..4ec1f619a9e7 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -4574,6 +4574,8 @@ final class ActivityStack { } r.configChangeFlags = 0; + r.deferRelaunchUntilPaused = false; + r.preserveWindowOnDeferredRelaunch = false; } boolean willActivityBeVisibleLocked(IBinder token) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 0d70e9901f68..811b48fc4fe5 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2738,12 +2738,21 @@ public final class ActivityStackSupervisor implements DisplayListener { // Called when WindowManager has finished animating the launchingBehind activity to the back. void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { - r.mLaunchTaskBehind = false; final TaskRecord task = r.task; - task.setLastThumbnailLocked(task.stack.screenshotActivitiesLocked(r)); + final ActivityStack stack = task.stack; + + r.mLaunchTaskBehind = false; + task.setLastThumbnailLocked(stack.screenshotActivitiesLocked(r)); mRecentTasks.addLocked(task); mService.notifyTaskStackChangedLocked(); mWindowManager.setAppVisibility(r.appToken, false); + + // When launching tasks behind, update the last active time of the top task after the new + // task has been shown briefly + final ActivityRecord top = stack.topActivity(); + if (top != null) { + top.task.touchActiveTime(); + } } void scheduleLaunchTaskBehindComplete(IBinder token) { diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index b157070b1184..e32d1d1fa19e 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -285,6 +285,7 @@ final class TaskRecord { mCallingPackage = info.packageName; setIntent(_intent, info); setMinDimensions(info); + touchActiveTime(); } TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent, @@ -315,6 +316,7 @@ final class TaskRecord { taskType = APPLICATION_ACTIVITY_TYPE; mTaskToReturnTo = HOME_ACTIVITY_TYPE; lastTaskDescription = _taskDescription; + touchActiveTime(); } private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1ed7070d2586..8f8afd5615f3 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -103,7 +103,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; // Brightness animation ramp rate in brightness units per second. - private static final int BRIGHTNESS_RAMP_RATE_FAST = 200; private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40; private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0; @@ -244,6 +243,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private boolean mAppliedDimming; private boolean mAppliedLowPower; + // Brightness ramp rate fast. + private final int mBrightnessRampRateFast; + // The controller for the automatic brightness level. private AutomaticBrightnessController mAutomaticBrightnessController; @@ -303,6 +305,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean( com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing); + mBrightnessRampRateFast = resources.getInteger( + com.android.internal.R.integer.config_brightness_ramp_rate_fast); + int lightSensorRate = resources.getInteger( com.android.internal.R.integer.config_autoBrightnessLightSensorRate); long brighteningLightDebounce = resources.getInteger( @@ -698,7 +703,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (!mPendingScreenOff) { if (state == Display.STATE_ON || state == Display.STATE_DOZE) { animateScreenBrightness(brightness, - slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); + slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : mBrightnessRampRateFast); } else { animateScreenBrightness(brightness, 0); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java index 9cfb590c9234..d339f69d3e28 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java @@ -24,7 +24,6 @@ import static com.android.server.net.NetworkPolicyManagerService.TAG; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -35,7 +34,6 @@ import android.net.NetworkPolicy; import android.net.NetworkTemplate; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; -import android.os.Binder; import android.os.RemoteException; import android.os.ShellCommand; import android.util.Log; @@ -88,12 +86,10 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { pw.println(" Adds a UID to the whitelist for restrict background usage."); pw.println(" add restrict-background-blacklist UID"); pw.println(" Adds a UID to the blacklist for restrict background usage."); - pw.println(" get metered-network ID"); - pw.println(" Checks whether the given non-mobile network is metered or not."); pw.println(" get restrict-background"); pw.println(" Gets the global restrict background usage status."); - pw.println(" list metered-networks [BOOLEAN]"); - pw.println(" Lists all non-mobile networks and whether they are metered or not."); + pw.println(" list wifi-networks [BOOLEAN]"); + pw.println(" Lists all saved wifi networks and whether they are metered or not."); pw.println(" If a boolean argument is passed, filters just the metered (or unmetered)"); pw.println(" networks."); pw.println(" list restrict-background-whitelist"); @@ -105,7 +101,7 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { pw.println(" remove restrict-background-blacklist UID"); pw.println(" Removes a UID from the blacklist for restrict background usage."); pw.println(" set metered-network ID BOOLEAN"); - pw.println(" Toggles whether the given non-mobile network is metered."); + pw.println(" Toggles whether the given wi-fi network is metered."); pw.println(" set restrict-background BOOLEAN"); pw.println(" Sets the global restrict background usage status."); } @@ -118,8 +114,6 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return -1; } switch(type) { - case "metered-network": - return getMeteredWifiNetwork(); case "restrict-background": return getRestrictBackground(); } @@ -152,8 +146,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return -1; } switch(type) { - case "metered-networks": - return listMeteredWifiNetworks(); + case "wifi-networks": + return listWifiNetworks(); case "restrict-background-whitelist": return listRestrictBackgroundWhitelist(); case "restrict-background-blacklist": @@ -284,7 +278,7 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return 0; } - private int listMeteredWifiNetworks() throws RemoteException { + private int listWifiNetworks() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final String arg = getNextArg(); final Boolean filter = arg == null ? null : Boolean.valueOf(arg); @@ -299,23 +293,6 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return 0; } - private int getMeteredWifiNetwork() throws RemoteException { - final PrintWriter pw = getOutPrintWriter(); - final String id = getNextArg(); - if (id == null) { - pw.println("Error: didn't specify ID"); - return -1; - } - final List<NetworkPolicy> policies = getWifiPolicies(); - for (NetworkPolicy policy: policies) { - if (id.equals(getNetworkId(policy))) { - pw.println(policy.metered); - return 0; - } - } - return 0; - } - private int setMeteredWifiNetwork() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final String id = getNextArg(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 022b10fb27aa..124d7f1b9c97 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -120,6 +120,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; @@ -226,7 +227,7 @@ public class NotificationManagerService extends SystemService { private VrManagerInternal mVrManagerInternal; final IBinder mForegroundToken = new Binder(); - private WorkerHandler mHandler; + private Handler mHandler; private final HandlerThread mRankingThread = new HandlerThread("ranker", Process.THREAD_PRIORITY_BACKGROUND); @@ -572,33 +573,9 @@ public class NotificationManagerService extends SystemService { public void clearEffects() { synchronized (mNotificationList) { if (DBG) Slog.d(TAG, "clearEffects"); - - // sound - mSoundNotificationKey = null; - - long identity = Binder.clearCallingIdentity(); - try { - final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); - if (player != null) { - player.stopAsync(); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(identity); - } - - // vibrate - mVibrateNotificationKey = null; - identity = Binder.clearCallingIdentity(); - try { - mVibrator.cancel(); - } finally { - Binder.restoreCallingIdentity(identity); - } - - // light - mLights.clear(); - updateLightsLocked(); + clearSoundLocked(); + clearVibrateLocked(); + clearLightsLocked(); } } @@ -658,6 +635,36 @@ public class NotificationManagerService extends SystemService { } }; + private void clearSoundLocked() { + mSoundNotificationKey = null; + long identity = Binder.clearCallingIdentity(); + try { + final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); + if (player != null) { + player.stopAsync(); + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void clearVibrateLocked() { + mVibrateNotificationKey = null; + long identity = Binder.clearCallingIdentity(); + try { + mVibrator.cancel(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void clearLightsLocked() { + // light + mLights.clear(); + updateLightsLocked(); + } + private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -863,6 +870,26 @@ public class NotificationManagerService extends SystemService { super(context); } + @VisibleForTesting + void setAudioManager(AudioManager audioMananger) { + mAudioManager = audioMananger; + } + + @VisibleForTesting + void setVibrator(Vibrator vibrator) { + mVibrator = vibrator; + } + + @VisibleForTesting + void setSystemReady(boolean systemReady) { + mSystemReady = systemReady; + } + + @VisibleForTesting + void setHandler(Handler handler) { + mHandler = handler; + } + @Override public void onStart() { Resources resources = getContext().getResources(); @@ -2492,12 +2519,14 @@ public class NotificationManagerService extends SystemService { return false; } - private void buzzBeepBlinkLocked(NotificationRecord record) { + @VisibleForTesting + void buzzBeepBlinkLocked(NotificationRecord record) { boolean buzz = false; boolean beep = false; boolean blink = false; final Notification notification = record.sbn.getNotification(); + final String key = record.getKey(); // Should this notification make noise, vibe, or use the LED? final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT; @@ -2521,9 +2550,15 @@ public class NotificationManagerService extends SystemService { if (disableEffects != null) { ZenLog.traceDisableEffects(record, disableEffects); } + + // Remember if this notification already owns the notification channels. + boolean wasBeep = key != null && key.equals(mSoundNotificationKey); + boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey); + + // These are set inside the conditional if the notification is allowed to make noise. + boolean hasValidVibrate = false; + boolean hasValidSound = false; if (disableEffects == null - && (!(record.isUpdate - && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) && (record.getUserId() == UserHandle.USER_ALL || record.getUserId() == currentUser || mUserProfiles.isCurrentProfile(record.getUserId())) @@ -2532,10 +2567,6 @@ public class NotificationManagerService extends SystemService { && mAudioManager != null) { if (DBG) Slog.v(TAG, "Interrupting!"); - sendAccessibilityEvent(notification, record.sbn.getPackageName()); - - // sound - // should we use the default notification sound? (indicated either by // DEFAULT_SOUND or because notification.sound is pointing at // Settings.System.NOTIFICATION_SOUND) @@ -2545,8 +2576,6 @@ public class NotificationManagerService extends SystemService { .equals(notification.sound); Uri soundUri = null; - boolean hasValidSound = false; - if (useDefaultSound) { soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; @@ -2559,88 +2588,105 @@ public class NotificationManagerService extends SystemService { hasValidSound = (soundUri != null); } - if (hasValidSound) { - boolean looping = - (notification.flags & Notification.FLAG_INSISTENT) != 0; - AudioAttributes audioAttributes = audioAttributesForNotification(notification); - mSoundNotificationKey = record.getKey(); - // do not play notifications if stream volume is 0 (typically because - // ringer mode is silent) or if there is a user of exclusive audio focus - if ((mAudioManager.getStreamVolume( - AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) - && !mAudioManager.isAudioFocusExclusive()) { - final long identity = Binder.clearCallingIdentity(); - try { - final IRingtonePlayer player = - mAudioManager.getRingtonePlayer(); - if (player != null) { - if (DBG) Slog.v(TAG, "Playing sound " + soundUri - + " with attributes " + audioAttributes); - player.playAsync(soundUri, record.sbn.getUser(), looping, - audioAttributes); - beep = true; - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - // vibrate // Does the notification want to specify its own vibration? final boolean hasCustomVibrate = notification.vibrate != null; // new in 4.2: if there was supposed to be a sound and we're in vibrate // mode, and no other vibration is specified, we fall back to vibration final boolean convertSoundToVibration = - !hasCustomVibrate - && hasValidSound - && (mAudioManager.getRingerModeInternal() - == AudioManager.RINGER_MODE_VIBRATE); + !hasCustomVibrate + && hasValidSound + && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE); // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. final boolean useDefaultVibrate = (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; - if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) - && !(mAudioManager.getRingerModeInternal() - == AudioManager.RINGER_MODE_SILENT)) { - mVibrateNotificationKey = record.getKey(); + hasValidVibrate = useDefaultVibrate || convertSoundToVibration || + hasCustomVibrate; + + // We can alert, and we're allowed to alert, but if the developer asked us to only do + // it once, and we already have, then don't. + if (!(record.isUpdate + && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) { + + sendAccessibilityEvent(notification, record.sbn.getPackageName()); + + if (hasValidSound) { + boolean looping = + (notification.flags & Notification.FLAG_INSISTENT) != 0; + AudioAttributes audioAttributes = audioAttributesForNotification(notification); + mSoundNotificationKey = key; + // do not play notifications if stream volume is 0 (typically because + // ringer mode is silent) or if there is a user of exclusive audio focus + if ((mAudioManager.getStreamVolume( + AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) + && !mAudioManager.isAudioFocusExclusive()) { + final long identity = Binder.clearCallingIdentity(); + try { + final IRingtonePlayer player = + mAudioManager.getRingtonePlayer(); + if (player != null) { + if (DBG) Slog.v(TAG, "Playing sound " + soundUri + + " with attributes " + audioAttributes); + player.playAsync(soundUri, record.sbn.getUser(), looping, + audioAttributes); + beep = true; + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } - if (useDefaultVibrate || convertSoundToVibration) { - // Escalate privileges so we can use the vibrator even if the - // notifying app does not have the VIBRATE permission. - long identity = Binder.clearCallingIdentity(); - try { + if (hasValidVibrate && !(mAudioManager.getRingerModeInternal() + == AudioManager.RINGER_MODE_SILENT)) { + mVibrateNotificationKey = key; + + if (useDefaultVibrate || convertSoundToVibration) { + // Escalate privileges so we can use the vibrator even if the + // notifying app does not have the VIBRATE permission. + long identity = Binder.clearCallingIdentity(); + try { + mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), + useDefaultVibrate ? mDefaultVibrationPattern + : mFallbackVibrationPattern, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) + ? 0: -1, audioAttributesForNotification(notification)); + buzz = true; + } finally { + Binder.restoreCallingIdentity(identity); + } + } else if (notification.vibrate.length > 1) { + // If you want your own vibration pattern, you need the VIBRATE + // permission mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), - useDefaultVibrate ? mDefaultVibrationPattern - : mFallbackVibrationPattern, - ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1, audioAttributesForNotification(notification)); + notification.vibrate, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) + ? 0: -1, audioAttributesForNotification(notification)); buzz = true; - } finally { - Binder.restoreCallingIdentity(identity); } - } else if (notification.vibrate.length > 1) { - // If you want your own vibration pattern, you need the VIBRATE - // permission - mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), - notification.vibrate, - ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1, audioAttributesForNotification(notification)); - buzz = true; } } + + } + // If a notification is updated to remove the actively playing sound or vibrate, + // cancel that feedback now + if (wasBeep && !hasValidSound) { + clearSoundLocked(); + } + if (wasBuzz && !hasValidVibrate) { + clearVibrateLocked(); } // light // release the light - boolean wasShowLights = mLights.remove(record.getKey()); + boolean wasShowLights = mLights.remove(key); if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold && ((record.getSuppressedVisualEffects() & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) { - mLights.add(record.getKey()); + mLights.add(key); updateLightsLocked(); if (mUseAttentionLight) { mAttentionLight.pulse(); @@ -2654,7 +2700,7 @@ public class NotificationManagerService extends SystemService { & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) { if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on"); } else { - EventLogTags.writeNotificationAlert(record.getKey(), + EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0); mHandler.post(mBuzzBeepBlinked); } diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 13a96ae50cbf..e496132a7451 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -247,10 +247,8 @@ final class DefaultPermissionGrantPolicy { } // SetupWizard - Intent setupIntent = new Intent(Intent.ACTION_MAIN); - setupIntent.addCategory(Intent.CATEGORY_SETUP_WIZARD); - PackageParser.Package setupPackage = getDefaultSystemHandlerActivityPackageLPr( - setupIntent, userId); + PackageParser.Package setupPackage = getSystemPackageLPr( + mService.mSetupWizardPackage); if (setupPackage != null && doesPackageSupportRuntimePermissions(setupPackage)) { grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 58592ff92ce9..0a86a849653c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -64,6 +64,7 @@ import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; +import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN; @@ -1034,6 +1035,7 @@ public class PackageManagerService extends IPackageManager.Stub { final @Nullable String mRequiredVerifierPackage; final @Nullable String mRequiredInstallerPackage; + final @Nullable String mSetupWizardPackage; private final PackageUsage mPackageUsage = new PackageUsage(); @@ -2529,6 +2531,7 @@ public class PackageManagerService extends IPackageManager.Stub { } mInstallerService = new PackageInstallerService(context, this); + mSetupWizardPackage = getSetupWizardPackageName(); final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr(); final ComponentName ephemeralInstallerComponent = getEphemeralInstallerLPr(); @@ -2886,12 +2889,15 @@ public class PackageManagerService extends IPackageManager.Stub { return cur; } - PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) { + private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - final PackageSetting ps = (PackageSetting) p.mExtras; if (ps == null) { return null; } + final PackageParser.Package p = ps.pkg; + if (p == null) { + return null; + } final PermissionsState permissionsState = ps.getPermissionsState(); @@ -2961,14 +2967,28 @@ public class PackageManagerService extends IPackageManager.Stub { false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader synchronized (mPackages) { - PackageParser.Package p = mPackages.get(packageName); + final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0; + PackageParser.Package p = null; + if (matchFactoryOnly) { + final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName); + if (ps != null) { + return generatePackageInfo(ps, flags, userId); + } + } + if (p == null) { + p = mPackages.get(packageName); + if (matchFactoryOnly && !isSystemApp(p)) { + return null; + } + } if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getPackageInfo " + packageName + ": " + p); if (p != null) { - return generatePackageInfo(p, flags, userId); + return generatePackageInfo((PackageSetting)p.mExtras, flags, userId); } - if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) { - return generatePackageInfoFromSettingsLPw(packageName, flags, userId); + if (!matchFactoryOnly && (flags & MATCH_UNINSTALLED_PACKAGES) != 0) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + return generatePackageInfo(ps, flags, userId); } } return null; @@ -3129,8 +3149,7 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { if (ps.pkg == null) { - PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, - flags, userId); + final PackageInfo pInfo = generatePackageInfo(ps, flags, userId); if (pInfo != null) { return pInfo.applicationInfo; } @@ -3142,31 +3161,6 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } - private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags, - int userId) { - if (!sUserManager.exists(userId)) return null; - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps != null) { - PackageParser.Package pkg = ps.pkg; - if (pkg == null) { - if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0) { - return null; - } - // Only data remains, so we aren't worried about code paths - pkg = new PackageParser.Package(packageName); - pkg.applicationInfo.packageName = packageName; - pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; - pkg.applicationInfo.privateFlags = ps.pkgPrivateFlags; - pkg.applicationInfo.uid = ps.appId; - pkg.applicationInfo.initForUser(userId); - pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString; - pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString; - } - return generatePackageInfo(pkg, flags, userId); - } - return null; - } - @Override public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; @@ -5915,11 +5909,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (listUninstalled) { list = new ArrayList<PackageInfo>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { - PackageInfo pi; + final PackageInfo pi; if (ps.pkg != null) { - pi = generatePackageInfo(ps.pkg, flags, userId); + pi = generatePackageInfo(ps, flags, userId); } else { - pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId); + pi = generatePackageInfo(ps, flags, userId); } if (pi != null) { list.add(pi); @@ -5928,7 +5922,8 @@ public class PackageManagerService extends IPackageManager.Stub { } else { list = new ArrayList<PackageInfo>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { - PackageInfo pi = generatePackageInfo(p, flags, userId); + final PackageInfo pi = + generatePackageInfo((PackageSetting)p.mExtras, flags, userId); if (pi != null) { list.add(pi); } @@ -5955,11 +5950,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (numMatch == 0) { return; } - PackageInfo pi; + final PackageInfo pi; if (ps.pkg != null) { - pi = generatePackageInfo(ps.pkg, flags, userId); + pi = generatePackageInfo(ps, flags, userId); } else { - pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId); + pi = generatePackageInfo(ps, flags, userId); } // The above might return null in cases of uninstalled apps or install-state // skew across users/profiles. @@ -9704,6 +9699,12 @@ public class PackageManagerService extends IPackageManager.Stub { // is granted only if it was already granted. allowed = origPermissions.hasInstallPermission(perm); } + if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0 + && pkg.packageName.equals(mSetupWizardPackage)) { + // If this permission is to be granted to the system setup wizard and + // this app is a setup wizard, then it gets the permission. + allowed = true; + } } return allowed; } @@ -16626,6 +16627,21 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); set, comp, userId); } + private @Nullable String getSetupWizardPackageName() { + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_SETUP_WIZARD); + + final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, + MATCH_SYSTEM_ONLY | MATCH_DISABLED_COMPONENTS, UserHandle.myUserId()); + if (matches.size() == 1) { + return matches.get(0).getComponentInfo().packageName; + } else { + Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size() + + ": matches=" + matches); + return null; + } + } + @Override public void setApplicationEnabledSetting(String appPackageName, int newState, int flags, int userId, String callingPackage) { diff --git a/services/core/java/com/android/server/pm/ShortcutBackupAgent.java b/services/core/java/com/android/server/pm/ShortcutBackupAgent.java deleted file mode 100644 index a0fbc37a4ace..000000000000 --- a/services/core/java/com/android/server/pm/ShortcutBackupAgent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.pm; - -import android.app.backup.BlobBackupHelper; - -public class ShortcutBackupAgent extends BlobBackupHelper { - private static final String TAG = "ShortcutBackupAgent"; - private static final int BLOB_VERSION = 1; - - public ShortcutBackupAgent(int currentBlobVersion, String... keys) { - super(currentBlobVersion, keys); - } - - @Override - protected byte[] getBackupPayload(String key) { - throw new RuntimeException("not implemented yet"); // todo - } - - @Override - protected void applyRestoredPayload(String key, byte[] payload) { - throw new RuntimeException("not implemented yet"); // todo - } -} diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java index 7699f305fb17..c6d66fe7fee5 100644 --- a/services/core/java/com/android/server/pm/ShortcutLauncher.java +++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java @@ -20,6 +20,10 @@ import android.annotation.UserIdInt; import android.content.pm.ShortcutInfo; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.ShortcutUser.PackageWithUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -27,6 +31,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; /** @@ -43,15 +48,16 @@ class ShortcutLauncher extends ShortcutPackageItem { private static final String ATTR_LAUNCHER_USER_ID = "launcher-user"; private static final String ATTR_VALUE = "value"; private static final String ATTR_PACKAGE_NAME = "package-name"; + private static final String ATTR_PACKAGE_USER_ID = "package-user"; private final int mOwnerUserId; /** * Package name -> IDs. */ - final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>(); + final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>(); - public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName, + private ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName, @UserIdInt int launcherUserId, ShortcutPackageInfo spi) { super(launcherUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty()); mOwnerUserId = ownerUserId; @@ -59,7 +65,7 @@ class ShortcutLauncher extends ShortcutPackageItem { public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName, @UserIdInt int launcherUserId) { - this(launcherUserId, packageName, launcherUserId, null); + this(ownerUserId, packageName, launcherUserId, null); } @Override @@ -67,16 +73,38 @@ class ShortcutLauncher extends ShortcutPackageItem { return mOwnerUserId; } + /** + * Called when the new package can't receive the backup, due to signature or version mismatch. + */ + @Override + protected void onRestoreBlocked(ShortcutService s) { + final ArrayList<PackageWithUser> pinnedPackages = + new ArrayList<>(mPinnedShortcuts.keySet()); + mPinnedShortcuts.clear(); + for (int i = pinnedPackages.size() - 1; i >= 0; i--) { + final PackageWithUser pu = pinnedPackages.get(i); + s.getPackageShortcutsLocked(pu.packageName, pu.userId) + .refreshPinnedFlags(s); + } + } + + @Override + protected void onRestored(ShortcutService s) { + // Nothing to do. + } + public void pinShortcuts(@NonNull ShortcutService s, @UserIdInt int packageUserId, @NonNull String packageName, @NonNull List<String> ids) { final ShortcutPackage packageShortcuts = s.getPackageShortcutsLocked(packageName, packageUserId); + final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName); + final int idSize = ids.size(); if (idSize == 0) { - mPinnedShortcuts.remove(packageName); + mPinnedShortcuts.remove(pu); } else { - final ArraySet<String> prevSet = mPinnedShortcuts.get(packageName); + final ArraySet<String> prevSet = mPinnedShortcuts.get(pu); // Pin shortcuts. Make sure only pin the ones that were visible to the caller. // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here. @@ -93,7 +121,7 @@ class ShortcutLauncher extends ShortcutPackageItem { newSet.add(id); } } - mPinnedShortcuts.put(packageName, newSet); + mPinnedShortcuts.put(pu, newSet); } packageShortcuts.refreshPinnedFlags(s); } @@ -101,12 +129,13 @@ class ShortcutLauncher extends ShortcutPackageItem { /** * Return the pinned shortcut IDs for the publisher package. */ - public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName) { - return mPinnedShortcuts.get(packageName); + public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName, + @UserIdInt int packageUserId) { + return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)); } - boolean cleanUpPackage(String packageName) { - return mPinnedShortcuts.remove(packageName) != null; + boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) { + return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null; } /** @@ -126,9 +155,15 @@ class ShortcutLauncher extends ShortcutPackageItem { getPackageInfo().saveToXml(out); for (int i = 0; i < size; i++) { + final PackageWithUser pu = mPinnedShortcuts.keyAt(i); + + if (forBackup && (pu.userId != getOwnerUserId())) { + continue; // Target package on a different user, skip. (i.e. work profile) + } + out.startTag(null, TAG_PACKAGE); - ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, - mPinnedShortcuts.keyAt(i)); + ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName); + ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId); final ArraySet<String> ids = mPinnedShortcuts.valueAt(i); final int idSize = ids.size(); @@ -157,8 +192,6 @@ class ShortcutLauncher extends ShortcutPackageItem { final ShortcutLauncher ret = new ShortcutLauncher(launcherUserId, launcherPackageName, launcherUserId); - ShortcutPackageInfo spi = null; - ArraySet<String> ids = null; final int outerDepth = parser.getDepth(); int type; @@ -172,13 +205,17 @@ class ShortcutLauncher extends ShortcutPackageItem { if (depth == outerDepth + 1) { switch (tag) { case ShortcutPackageInfo.TAG_ROOT: - spi = ShortcutPackageInfo.loadFromXml(parser); + ret.getPackageInfo().loadFromXml(parser, fromBackup); continue; case TAG_PACKAGE: { final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_PACKAGE_NAME); + final int packageUserId = fromBackup ? ownerUserId + : ShortcutService.parseIntAttribute(parser, + ATTR_PACKAGE_USER_ID, ownerUserId); ids = new ArraySet<>(); - ret.mPinnedShortcuts.put(packageName, ids); + ret.mPinnedShortcuts.put( + PackageWithUser.of(packageUserId, packageName), ids); continue; } } @@ -186,17 +223,17 @@ class ShortcutLauncher extends ShortcutPackageItem { if (depth == outerDepth + 2) { switch (tag) { case TAG_PIN: { - ids.add(ShortcutService.parseStringAttribute(parser, - ATTR_VALUE)); + if (ids == null) { + Slog.w(TAG, TAG_PIN + " in invalid place"); + } else { + ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE)); + } continue; } } } ShortcutService.warnForInvalidTag(depth, tag); } - if (spi != null) { - ret.replacePackageInfo(spi); - } return ret; } @@ -208,6 +245,8 @@ class ShortcutLauncher extends ShortcutPackageItem { pw.print(getPackageName()); pw.print(" Package user: "); pw.print(getPackageUserId()); + pw.print(" Owner user: "); + pw.print(getOwnerUserId()); pw.println(); getPackageInfo().dump(s, pw, prefix + " "); @@ -217,10 +256,14 @@ class ShortcutLauncher extends ShortcutPackageItem { for (int i = 0; i < size; i++) { pw.println(); + final PackageWithUser pu = mPinnedShortcuts.keyAt(i); + pw.print(prefix); pw.print(" "); pw.print("Package: "); - pw.println(mPinnedShortcuts.keyAt(i)); + pw.print(pu.packageName); + pw.print(" User: "); + pw.println(pu.userId); final ArraySet<String> ids = mPinnedShortcuts.valueAt(i); final int idSize = ids.size(); @@ -233,4 +276,9 @@ class ShortcutLauncher extends ShortcutPackageItem { } } } + + @VisibleForTesting + ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) { + return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName))); + } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 5916202a0bdb..1076a7a0ae88 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -26,6 +26,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -34,6 +36,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.function.Predicate; @@ -83,7 +86,7 @@ class ShortcutPackage extends ShortcutPackageItem { */ private long mLastResetTime; - public ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) { + private ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) { super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty()); } @@ -97,6 +100,19 @@ class ShortcutPackage extends ShortcutPackageItem { return getPackageUserId(); } + @Override + protected void onRestoreBlocked(ShortcutService s) { + // Can't restore due to version/signature mismatch. Remove all shortcuts. + mShortcuts.clear(); + } + + @Override + protected void onRestored(ShortcutService s) { + // Because some launchers may not have been restored (e.g. allowBackup=false), + // we need to re-calculate the pinned shortcuts. + refreshPinnedFlags(s); + } + /** * Note this does *not* provide a correct view to the calling launcher. */ @@ -229,20 +245,26 @@ class ShortcutPackage extends ShortcutPackageItem { s.getUserShortcutsLocked(getPackageUserId()).getAllLaunchers(); for (int l = launchers.size() - 1; l >= 0; l--) { + // Note even if a launcher that hasn't been installed can still pin shortcuts. + final ShortcutLauncher launcherShortcuts = launchers.valueAt(l); final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds( - getPackageName()); + getPackageName(), getPackageUserId()); if (pinned == null || pinned.size() == 0) { continue; } for (int i = pinned.size() - 1; i >= 0; i--) { - final ShortcutInfo si = mShortcuts.get(pinned.valueAt(i)); + final String id = pinned.valueAt(i); + final ShortcutInfo si = mShortcuts.get(id); if (si == null) { - s.wtf("Shortcut not found"); - } else { - si.addFlags(ShortcutInfo.FLAG_PINNED); + // This happens if a launcher pinned shortcuts from this package, then backup& + // restored, but this package doesn't allow backing up. + // In that case the launcher ends up having a dangling pinned shortcuts. + // That's fine, when the launcher is restored, we'll fix it. + continue; } + si.addFlags(ShortcutInfo.FLAG_PINNED); } } @@ -312,11 +334,15 @@ class ShortcutPackage extends ShortcutPackageItem { public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result, @Nullable Predicate<ShortcutInfo> query, int cloneFlag, @Nullable String callingLauncher, int launcherUserId) { + if (getPackageInfo().isShadow()) { + // Restored and the app not installed yet, so don't return any. + return; + } // Set of pinned shortcuts by the calling launcher. final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null - : s.getLauncherShortcuts(callingLauncher, getPackageUserId(), launcherUserId) - .getPinnedShortcutIds(getPackageName()); + : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId) + .getPinnedShortcutIds(getPackageName(), getPackageUserId()); for (int i = 0; i < mShortcuts.size(); i++) { final ShortcutInfo si = mShortcuts.valueAt(i); @@ -328,7 +354,8 @@ class ShortcutPackage extends ShortcutPackageItem { || ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId())); if (!si.isDynamic()) { if (!si.isPinned()) { - s.wtf("Shortcut not pinned here"); + s.wtf("Shortcut not pinned: package " + getPackageName() + + ", user=" + getPackageUserId() + ", id=" + si.getId()); continue; } if (!isPinnedByCaller) { @@ -479,7 +506,6 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); - ShortcutPackageInfo spi = null; final int outerDepth = parser.getDepth(); int type; @@ -493,7 +519,7 @@ class ShortcutPackage extends ShortcutPackageItem { if (depth == outerDepth + 1) { switch (tag) { case ShortcutPackageInfo.TAG_ROOT: - spi = ShortcutPackageInfo.loadFromXml(parser); + ret.getPackageInfo().loadFromXml(parser, fromBackup); continue; case TAG_SHORTCUT: final ShortcutInfo si = parseShortcut(parser, packageName); @@ -505,9 +531,6 @@ class ShortcutPackage extends ShortcutPackageItem { } ShortcutService.warnForInvalidTag(depth, tag); } - if (spi != null) { - ret.replacePackageInfo(spi); - } return ret; } @@ -567,4 +590,9 @@ class ShortcutPackage extends ShortcutPackageItem { intentPersistableExtras, weight, extras, lastChangedTimestamp, flags, iconRes, bitmapPath); } + + @VisibleForTesting + List<ShortcutInfo> getAllShortcutsForTest() { + return new ArrayList<>(mShortcuts.values()); + } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java index 5f706b83271e..2c45890a9c40 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java @@ -67,10 +67,6 @@ class ShortcutPackageInfo { return mIsShadow; } - public boolean isInstalled() { - return !mIsShadow; - } - public void setShadow(boolean shadow) { mIsShadow = shadow; } @@ -79,14 +75,24 @@ class ShortcutPackageInfo { return mVersionCode; } - public boolean canRestoreTo(PackageInfo target) { + public boolean hasSignatures() { + return mSigHashes.size() > 0; + } + + public boolean canRestoreTo(ShortcutService s, PackageInfo target) { + if (!s.shouldBackupApp(target)) { + // "allowBackup" was true when backed up, but now false. + Slog.w(TAG, "Can't restore: package no longer allows backup"); + return false; + } if (target.versionCode < mVersionCode) { - Slog.w(TAG, String.format("Package current version %d < backed up version %d", + Slog.w(TAG, String.format( + "Can't restore: package current version %d < backed up version %d", target.versionCode, mVersionCode)); return false; } if (!BackupUtils.signaturesMatch(mSigHashes, target)) { - Slog.w(TAG, "Package signature mismtach"); + Slog.w(TAG, "Can't restore: Package signature mismatch"); return false; } return true; @@ -106,6 +112,11 @@ class ShortcutPackageInfo { } public void refresh(ShortcutService s, ShortcutPackageItem pkg) { + if (mIsShadow) { + s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName() + + ", user=" + pkg.getOwnerUserId()); + return; + } // Note use mUserId here, rather than userId. final PackageInfo pi = s.getPackageInfoWithSignatures( pkg.getPackageName(), pkg.getPackageUserId()); @@ -132,14 +143,16 @@ class ShortcutPackageInfo { out.endTag(null, TAG_ROOT); } - public static ShortcutPackageInfo loadFromXml(XmlPullParser parser) + public void loadFromXml(XmlPullParser parser, boolean fromBackup) throws IOException, XmlPullParserException { final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION); - final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW); - final ArrayList<byte[]> hashes = new ArrayList<>(); + // When restoring from backup, it's always shadow. + final boolean shadow = + fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW); + final ArrayList<byte[]> hashes = new ArrayList<>(); final int outerDepth = parser.getDepth(); int type; @@ -163,7 +176,11 @@ class ShortcutPackageInfo { } ShortcutService.warnForInvalidTag(depth, tag); } - return new ShortcutPackageInfo(versionCode, hashes, shadow); + + // Successfully loaded; replace the feilds. + mVersionCode = versionCode; + mIsShadow = shadow; + mSigHashes = hashes; } public void dump(ShortcutService s, PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java index de2709d3d7ac..f31dd17d6288 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -16,6 +16,8 @@ package com.android.server.pm; import android.annotation.NonNull; +import android.content.pm.PackageInfo; +import android.util.Slog; import com.android.internal.util.Preconditions; @@ -25,10 +27,12 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; abstract class ShortcutPackageItem { + private static final String TAG = ShortcutService.TAG; + private final int mPackageUserId; private final String mPackageName; - private ShortcutPackageInfo mPackageInfo; + private final ShortcutPackageInfo mPackageInfo; protected ShortcutPackageItem(int packageUserId, @NonNull String packageName, @NonNull ShortcutPackageInfo packageInfo) { @@ -61,25 +65,62 @@ abstract class ShortcutPackageItem { return mPackageInfo; } - /** - * Should be only used when loading from a file.o - */ - protected void replacePackageInfo(@NonNull ShortcutPackageInfo packageInfo) { - mPackageInfo = Preconditions.checkNotNull(packageInfo); - } - public void refreshPackageInfoAndSave(ShortcutService s) { + if (mPackageInfo.isShadow()) { + return; // Don't refresh for shadow user. + } mPackageInfo.refresh(s, this); s.scheduleSaveUser(getOwnerUserId()); } - public void ensureNotShadowAndSave(ShortcutService s) { - if (mPackageInfo.isShadow()) { - mPackageInfo.setShadow(false); - s.scheduleSaveUser(getOwnerUserId()); + public void attemptToRestoreIfNeededAndSave(ShortcutService s) { + if (!mPackageInfo.isShadow()) { + return; // Already installed, nothing to do. } + if (!s.isPackageInstalled(mPackageName, mPackageUserId)) { + if (ShortcutService.DEBUG) { + Slog.d(TAG, String.format("Package still not installed: %s user=%d", + mPackageName, mPackageUserId)); + } + return; // Not installed, no need to restore yet. + } + if (!mPackageInfo.hasSignatures()) { + s.wtf("Attempted to restore package " + mPackageName + ", user=" + mPackageUserId + + " but signatures not found in the restore data."); + onRestoreBlocked(s); + return; + } + + final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId); + if (!mPackageInfo.canRestoreTo(s, pi)) { + // Package is now installed, but can't restore. Let the subclass do the cleanup. + onRestoreBlocked(s); + return; + } + if (ShortcutService.DEBUG) { + Slog.d(TAG, String.format("Restored package: %s/%d on user %d", mPackageName, + mPackageUserId, getOwnerUserId())); + } + + onRestored(s); + + // Now the package is not shadow. + mPackageInfo.setShadow(false); + + s.scheduleSaveUser(mPackageUserId); } + /** + * Called when the new package can't be restored because it has a lower version number + * or different signatures. + */ + protected abstract void onRestoreBlocked(ShortcutService s); + + /** + * Called when the new package is successfully restored. + */ + protected abstract void onRestored(ShortcutService s); + public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException; } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 76a2dfadc264..7aefcb82a158 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -46,6 +46,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Binder; import android.os.Environment; +import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; @@ -101,6 +102,7 @@ import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -112,17 +114,16 @@ import java.util.function.Predicate; * * - Scan and remove orphan bitmaps (just in case). * - * - Backup & restore - * * - Detect when already registered instances are passed to APIs again, which might break * internal bitmap handling. + * + * - Add more call stats. */ public class ShortcutService extends IShortcutService.Stub { static final String TAG = "ShortcutService"; static final boolean DEBUG = false; // STOPSHIP if true static final boolean DEBUG_LOAD = false; // STOPSHIP if true - static final boolean ENABLE_DEBUG_COMMAND = true; // STOPSHIP if true @VisibleForTesting static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day @@ -262,6 +263,26 @@ public class ShortcutService extends IShortcutService.Stub { | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES; + // Stats + @VisibleForTesting + interface Stats { + int GET_DEFAULT_HOME = 0; + int GET_PACKAGE_INFO = 1; + int GET_PACKAGE_INFO_WITH_SIG = 2; + int GET_APPLICATION_INFO = 3; + int LAUNCHER_PERMISSION_CHECK = 4; + + int COUNT = LAUNCHER_PERMISSION_CHECK + 1; + } + + final Object mStatLock = new Object(); + + @GuardedBy("mStatLock") + private final int[] mCountStats = new int[Stats.COUNT]; + + @GuardedBy("mStatLock") + private final long[] mDurationStats = new long[Stats.COUNT]; + public ShortcutService(Context context) { this(context, BackgroundThread.get().getLooper()); } @@ -278,6 +299,13 @@ public class ShortcutService extends IShortcutService.Stub { mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false); } + void logDurationStat(int statId, long start) { + synchronized (mStatLock) { + mCountStats[statId]++; + mDurationStats[statId] += (System.currentTimeMillis() - start); + } + } + /** * System service lifecycle. */ @@ -822,7 +850,7 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") @NonNull - boolean isUserLoadedLocked(@UserIdInt int userId) { + private boolean isUserLoadedLocked(@UserIdInt int userId) { return mUsers.get(userId) != null; } @@ -841,19 +869,27 @@ public class ShortcutService extends IShortcutService.Stub { return userPackages; } + void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) { + for (int i = mUsers.size() - 1; i >= 0; i--) { + c.accept(mUsers.valueAt(i)); + } + } + /** Return the per-user per-package state. */ @GuardedBy("mLock") @NonNull ShortcutPackage getPackageShortcutsLocked( @NonNull String packageName, @UserIdInt int userId) { - return getUserShortcutsLocked(userId).getPackageShortcuts(packageName); + return getUserShortcutsLocked(userId).getPackageShortcuts(this, packageName); } @GuardedBy("mLock") @NonNull - ShortcutLauncher getLauncherShortcuts( - @NonNull String packageName, @UserIdInt int userId, @UserIdInt int launcherUserId) { - return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName, launcherUserId); + ShortcutLauncher getLauncherShortcutsLocked( + @NonNull String packageName, @UserIdInt int ownerUserId, + @UserIdInt int launcherUserId) { + return getUserShortcutsLocked(ownerUserId) + .getLauncherShortcuts(this, packageName, launcherUserId); } // === Caller validation === @@ -1212,8 +1248,6 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); - ps.ensureNotShadowAndSave(this); - // Throttling. if (!ps.tryApiCall(this)) { return false; @@ -1249,8 +1283,6 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); - ps.ensureNotShadowAndSave(this); - // Throttling. if (!ps.tryApiCall(this)) { return false; @@ -1288,8 +1320,6 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); - ps.ensureNotShadowAndSave(this); - // Throttling. if (!ps.tryApiCall(this)) { return false; @@ -1422,15 +1452,17 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { synchronized (mLock) { - long start = System.currentTimeMillis(); + final long start = System.currentTimeMillis(); final ShortcutUser user = getUserShortcutsLocked(userId); final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); // Default launcher from package manager. + final long startGetHomeActivitiesAsUser = System.currentTimeMillis(); final ComponentName defaultLauncher = injectPackageManagerInternal() .getHomeActivitiesAsUser(allHomeCandidates, userId); + logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); ComponentName detected; if (defaultLauncher != null) { @@ -1473,10 +1505,8 @@ public class ShortcutService extends IShortcutService.Stub { lastPriority = ri.priority; } } - final long end = System.currentTimeMillis(); - if (DEBUG) { - Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start)); - } + logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); + if (detected != null) { if (DEBUG) { Slog.v(TAG, "Detected launcher: " + detected); @@ -1492,10 +1522,17 @@ public class ShortcutService extends IShortcutService.Stub { // === House keeping === + /** + * Remove all the information associated with a package. This will really remove all the + * information, including the restore information (i.e. it'll remove packages even if they're + * shadow). + */ @VisibleForTesting void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) { - - // TODO Don't remove shadow packages' information. + if (isPackageInstalled(packageName, packageUserId)) { + wtf("Package " + packageName + " is still installed for user " + packageUserId); + return; + } final boolean wasUserLoaded = isUserLoadedLocked(owningUserId); @@ -1504,7 +1541,7 @@ public class ShortcutService extends IShortcutService.Stub { // First, remove the package from the package list (if the package is a publisher). if (packageUserId == owningUserId) { - if (mUser.getPackages().remove(packageName) != null) { + if (mUser.removePackage(packageName) != null) { doNotify = true; } } @@ -1515,12 +1552,12 @@ public class ShortcutService extends IShortcutService.Stub { // Then remove pinned shortcuts from all launchers. final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers(); for (int i = launchers.size() - 1; i >= 0; i--) { - launchers.valueAt(i).cleanUpPackage(packageName); + launchers.valueAt(i).cleanUpPackage(packageName, packageUserId); } // Now there may be orphan shortcuts because we removed pinned shortucts at the previous // step. Remove them too. - for (int i = mUser.getPackages().size() - 1; i >= 0; i--) { - mUser.getPackages().valueAt(i).refreshPinnedFlags(this); + for (int i = mUser.getAllPackages().size() - 1; i >= 0; i--) { + mUser.getAllPackages().valueAt(i).refreshPinnedFlags(this); } scheduleSaveUser(owningUserId); @@ -1539,6 +1576,7 @@ public class ShortcutService extends IShortcutService.Stub { * Entry point from {@link LauncherApps}. */ private class LocalService extends ShortcutServiceInternal { + @Override public List<ShortcutInfo> getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @@ -1551,13 +1589,16 @@ public class ShortcutService extends IShortcutService.Stub { : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO; synchronized (mLock) { + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(ShortcutService.this); + if (packageName != null) { getShortcutsInnerLocked(launcherUserId, callingPackage, packageName, changedSince, componentName, queryFlags, userId, ret, cloneFlag); } else { final ArrayMap<String, ShortcutPackage> packages = - getUserShortcutsLocked(userId).getPackages(); + getUserShortcutsLocked(userId).getAllPackages(); for (int i = packages.size() - 1; i >= 0; i--) { getShortcutsInnerLocked(launcherUserId, callingPackage, packages.keyAt(i), changedSince, @@ -1601,6 +1642,9 @@ public class ShortcutService extends IShortcutService.Stub { final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size()); final ArraySet<String> idSet = new ArraySet<>(ids); synchronized (mLock) { + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(ShortcutService.this); + getPackageShortcutsLocked(packageName, userId).findAll( ShortcutService.this, ret, (ShortcutInfo si) -> idSet.contains(si.getId()), @@ -1616,13 +1660,16 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); synchronized (mLock) { + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(ShortcutService.this); + final ShortcutInfo si = getShortcutInfoLocked( launcherUserId, callingPackage, packageName, shortcutId, userId); return si != null && si.isPinned(); } } - public ShortcutInfo getShortcutInfoLocked( + private ShortcutInfo getShortcutInfoLocked( int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId) { Preconditions.checkStringNotEmpty(packageName, "packageName"); @@ -1646,9 +1693,8 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { final ShortcutLauncher launcher = - getLauncherShortcuts(callingPackage, userId, launcherUserId); - - launcher.ensureNotShadowAndSave(ShortcutService.this); + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); + launcher.attemptToRestoreIfNeededAndSave(ShortcutService.this); launcher.pinShortcuts( ShortcutService.this, userId, packageName, shortcutIds); @@ -1665,6 +1711,9 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); synchronized (mLock) { + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(ShortcutService.this); + // Make sure the shortcut is actually visible to the launcher. final ShortcutInfo si = getShortcutInfoLocked( launcherUserId, callingPackage, packageName, shortcutId, userId); @@ -1690,6 +1739,9 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(shortcut, "shortcut"); synchronized (mLock) { + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(ShortcutService.this); + final ShortcutInfo shortcutInfo = getPackageShortcutsLocked( shortcut.getPackageName(), userId).findShortcutById(shortcut.getId()); return (shortcutInfo != null && shortcutInfo.hasIconResource()) @@ -1704,6 +1756,9 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(shortcutIn, "shortcut"); synchronized (mLock) { + getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) + .attemptToRestoreIfNeededAndSave(ShortcutService.this); + final ShortcutInfo shortcutInfo = getPackageShortcutsLocked( shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId()); if (shortcutInfo == null || !shortcutInfo.hasIconFile()) { @@ -1757,28 +1812,28 @@ public class ShortcutService extends IShortcutService.Stub { * perform cleanup. */ @VisibleForTesting - void cleanupGonePackages(@UserIdInt int userId) { + void cleanupGonePackages(@UserIdInt int ownerUserId) { if (DEBUG) { - Slog.d(TAG, "cleanupGonePackages() userId=" + userId); + Slog.d(TAG, "cleanupGonePackages() ownerUserId=" + ownerUserId); } final ArrayList<PackageWithUser> gonePackages = new ArrayList<>(); synchronized (mLock) { - final ShortcutUser user = getUserShortcutsLocked(userId); + final ShortcutUser user = getUserShortcutsLocked(ownerUserId); user.forAllPackageItems(spi -> { if (spi.getPackageInfo().isShadow()) { return; // Don't delete shadow information. } if (isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) { - return; + return; // Package not gone. } gonePackages.add(PackageWithUser.of(spi)); }); if (gonePackages.size() > 0) { for (int i = gonePackages.size() - 1; i >= 0; i--) { final PackageWithUser pu = gonePackages.get(i); - cleanUpPackageLocked(pu.packageName, userId, pu.userId); + cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId); } } } @@ -1789,7 +1844,8 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId)); } synchronized (mLock) { - getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId); + forEachLoadedUserLocked(user -> + user.attemptToRestoreIfNeededAndSave(this, packageName, userId)); } } @@ -1798,18 +1854,20 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d", packageName, userId)); } - synchronized (mLock) { - getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId); + forEachLoadedUserLocked(user -> + user.attemptToRestoreIfNeededAndSave(this, packageName, userId)); } } - private void handlePackageRemoved(String packageName, @UserIdInt int userId) { + private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) { if (DEBUG) { - Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId)); + Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, + packageUserId)); } synchronized (mLock) { - cleanUpPackageLocked(packageName, userId, userId); + forEachLoadedUserLocked(user -> + cleanUpPackageLocked(packageName, user.getUserId(), packageUserId)); } } @@ -1836,6 +1894,7 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId, boolean getSignatures) { + final long start = System.currentTimeMillis(); final long token = injectClearCallingIdentity(); try { return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS @@ -1847,11 +1906,16 @@ public class ShortcutService extends IShortcutService.Stub { return null; } finally { injectRestoreCallingIdentity(token); + + logDurationStat( + (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO), + start); } } @VisibleForTesting ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) { + final long start = System.currentTimeMillis(); final long token = injectClearCallingIdentity(); try { return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId); @@ -1861,6 +1925,8 @@ public class ShortcutService extends IShortcutService.Stub { return null; } finally { injectRestoreCallingIdentity(token); + + logDurationStat(Stats.GET_APPLICATION_INFO, start); } } @@ -1869,7 +1935,7 @@ public class ShortcutService extends IShortcutService.Stub { return (ai != null) && ((ai.flags & flags) == flags); } - private boolean isPackageInstalled(String packageName, int userId) { + boolean isPackageInstalled(String packageName, int userId) { return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED); } @@ -1879,8 +1945,12 @@ public class ShortcutService extends IShortcutService.Stub { return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP); } + boolean shouldBackupApp(PackageInfo pi) { + return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; + } + @Override - public byte[] getBackupPayload(@UserIdInt int userId) throws RemoteException { + public byte[] getBackupPayload(@UserIdInt int userId) { enforceSystem(); if (DEBUG) { Slog.d(TAG, "Backing up user " + userId); @@ -1908,7 +1978,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public void applyRestore(byte[] payload, @UserIdInt int userId) throws RemoteException { + public void applyRestore(byte[] payload, @UserIdInt int userId) { enforceSystem(); if (DEBUG) { Slog.d(TAG, "Restoring user " + userId); @@ -1923,6 +1993,15 @@ public class ShortcutService extends IShortcutService.Stub { } synchronized (mLock) { mUsers.put(userId, user); + + // Then purge all the save images. + final File bitmapPath = getUserBitmapFilePath(userId); + final boolean success = FileUtils.deleteContents(bitmapPath); + if (!success) { + Slog.w(TAG, "Failed to delete " + bitmapPath); + } + + saveUserLocked(userId); } } @@ -1974,9 +2053,19 @@ public class ShortcutService extends IShortcutService.Stub { pw.print(" Icon format: "); pw.print(mIconPersistFormat); pw.print(" Icon quality: "); - pw.print(mIconPersistQuality); + pw.println(mIconPersistQuality); pw.println(); + pw.println(" Stats:"); + synchronized (mStatLock) { + final String p = " "; + dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()"); + dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check"); + + dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()"); + dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)"); + dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo"); + } for (int i = 0; i < mUsers.size(); i++) { pw.println(); @@ -1991,6 +2080,15 @@ public class ShortcutService extends IShortcutService.Stub { return tobj.format("%Y-%m-%d %H:%M:%S"); } + private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) { + pw.print(prefix); + final int count = mCountStats[statId]; + final long dur = mDurationStats[statId]; + pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms", + label, count, dur, + (count == 0 ? 0 : ((double) dur) / count))); + } + // === Shell support === @Override @@ -2202,9 +2300,10 @@ public class ShortcutService extends IShortcutService.Stub { } final void wtf(String message) { - Slog.wtf(TAG, message, /* exception= */ null); + wtf( message, /* exception= */ null); } + // Injection point. void wtf(String message, Exception e) { Slog.wtf(TAG, message, e); } @@ -2275,7 +2374,7 @@ public class ShortcutService extends IShortcutService.Stub { final ShortcutUser user = mUsers.get(userId); if (user == null) return null; - final ShortcutPackage pkg = user.getPackages().get(packageName); + final ShortcutPackage pkg = user.getAllPackages().get(packageName); if (pkg == null) return null; return pkg.findShortcutById(shortcutId); diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index 487558f81e45..593f607f6268 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -53,8 +53,8 @@ class ShortcutUser { this.packageName = Preconditions.checkNotNull(packageName); } - public static PackageWithUser of(int launcherUserId, String packageName) { - return new PackageWithUser(launcherUserId, packageName); + public static PackageWithUser of(int userId, String packageName) { + return new PackageWithUser(userId, packageName); } public static PackageWithUser of(ShortcutPackageItem spi) { @@ -78,12 +78,12 @@ class ShortcutUser { @Override public String toString() { - return String.format("{Launcher: %d, %s}", userId, packageName); + return String.format("{Package: %d, %s}", userId, packageName); } } @UserIdInt - final int mUserId; + private final int mUserId; private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>(); @@ -95,10 +95,18 @@ class ShortcutUser { mUserId = userId; } - public ArrayMap<String, ShortcutPackage> getPackages() { + public int getUserId() { + return mUserId; + } + + public ArrayMap<String, ShortcutPackage> getAllPackages() { return mPackages; } + public ShortcutPackage removePackage(@NonNull String packageName) { + return mPackages.remove(packageName); + } + public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() { return mLaunchers; } @@ -113,22 +121,26 @@ class ShortcutUser { return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName)); } - public ShortcutPackage getPackageShortcuts(@NonNull String packageName) { + public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) { ShortcutPackage ret = mPackages.get(packageName); if (ret == null) { ret = new ShortcutPackage(mUserId, packageName); mPackages.put(packageName, ret); + } else { + ret.attemptToRestoreIfNeededAndSave(s); } return ret; } - public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName, + public ShortcutLauncher getLauncherShortcuts(ShortcutService s, @NonNull String packageName, @UserIdInt int launcherUserId) { final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName); ShortcutLauncher ret = mLaunchers.get(key); if (ret == null) { ret = new ShortcutLauncher(mUserId, packageName, launcherUserId); mLaunchers.put(key, ret); + } else { + ret.attemptToRestoreIfNeededAndSave(s); } return ret; } @@ -148,14 +160,6 @@ class ShortcutUser { } } - public void unshadowPackage(ShortcutService s, @NonNull String packageName, - @UserIdInt int packageUserId) { - forPackageItem(packageName, packageUserId, spi -> { - Slog.i(TAG, String.format("Restoring for %s, user=%d", packageName, packageUserId)); - spi.ensureNotShadowAndSave(s); - }); - } - public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId, Consumer<ShortcutPackageItem> callback) { forAllPackageItems(spi -> { @@ -166,6 +170,13 @@ class ShortcutUser { }); } + public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName, + @UserIdInt int packageUserId) { + forPackageItem(packageName, packageUserId, spi -> { + spi.attemptToRestoreIfNeededAndSave(s); + }); + } + public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { out.startTag(null, TAG_ROOT); @@ -229,7 +240,7 @@ class ShortcutUser { s, parser, userId, fromBackup); // Don't use addShortcut(), we don't need to save the icon. - ret.getPackages().put(shortcuts.getPackageName(), shortcuts); + ret.mPackages.put(shortcuts.getPackageName(), shortcuts); continue; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 88f4190ad9b7..3ff46acb5d31 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1941,6 +1941,7 @@ public class UserManagerService extends IUserManager.Stub { long now = System.currentTimeMillis(); userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; userInfo.partial = true; + userInfo.lastLoggedInFingerprint = Build.FINGERPRINT; userData = new UserData(); userData.info = userInfo; mUsers.put(userId, userData); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 747e31e5c2a8..14d0457e41c1 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3107,10 +3107,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return -1; } } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) { - if (down) { - if (repeatCount == 0) { - toggleKeyboardShortcutsMenu(event.getDeviceId()); - } + if (down && repeatCount == 0 && !isKeyguardLocked()) { + toggleKeyboardShortcutsMenu(event.getDeviceId()); } } else if (keyCode == KeyEvent.KEYCODE_ASSIST) { if (down) { diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java index 1363fb977134..eb926c1ab2c7 100644 --- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java +++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java @@ -227,10 +227,11 @@ public class EnabledComponentsObserver implements SettingChangeListener { return userIds; } - private ArraySet<ComponentName> loadComponentNamesForUser(int userId) { + public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId, + String serviceName, String permissionName) { + ArraySet<ComponentName> installed = new ArraySet<>(); - PackageManager pm = mContext.getPackageManager(); - Intent queryIntent = new Intent(mServiceName); + Intent queryIntent = new Intent(serviceName); List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( queryIntent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, @@ -241,10 +242,10 @@ public class EnabledComponentsObserver implements SettingChangeListener { ServiceInfo info = resolveInfo.serviceInfo; ComponentName component = new ComponentName(info.packageName, info.name); - if (!mServicePermission.equals(info.permission)) { + if (!permissionName.equals(info.permission)) { Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name + ": it does not require the permission " - + mServicePermission); + + permissionName); continue; } installed.add(component); @@ -253,6 +254,11 @@ public class EnabledComponentsObserver implements SettingChangeListener { return installed; } + private ArraySet<ComponentName> loadComponentNamesForUser(int userId) { + return loadComponentNames(mContext.getPackageManager(), userId, mServiceName, + mServicePermission); + } + private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, int userId) { final ContentResolver cr = mContext.getContentResolver(); diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index aa6f59ef6e9a..c572e766f1ec 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -16,16 +16,23 @@ package com.android.server.vr; import android.app.AppOpsManager; +import android.app.NotificationManager; import android.annotation.NonNull; -import android.content.Context; import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.IInterface; import android.os.Looper; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.Settings; +import android.service.notification.NotificationListenerService; import android.service.vr.IVrListener; import android.service.vr.VrListenerService; import android.util.ArraySet; @@ -38,7 +45,9 @@ import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeLis import com.android.server.utils.ManagedApplicationService; import com.android.server.utils.ManagedApplicationService.BinderChecker; +import java.lang.StringBuilder; import java.util.ArrayList; +import java.util.Collection; import java.util.Objects; import java.util.Set; @@ -53,8 +62,8 @@ import java.util.Set; * hardware/libhardware/modules/vr * <p/> * In general applications may enable or disable VR mode by calling - * {@link android.app.Activity#setVrMode)}. An application may also implement a service to be run - * while in VR mode by implementing {@link android.service.vr.VrListenerService}. + * {@link android.app.Activity#setVrModeEnabled)}. An application may also implement a service to + * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}. * * @see {@link android.service.vr.VrListenerService} * @see {@link com.android.server.vr.VrManagerInternal} @@ -74,13 +83,18 @@ public class VrManagerService extends SystemService implements EnabledComponentC private final IBinder mOverlayToken = new Binder(); // State protected by mLock - private boolean mVrModeEnabled = false; + private boolean mVrModeEnabled; private final Set<VrStateListener> mListeners = new ArraySet<>(); private EnabledComponentsObserver mComponentObserver; private ManagedApplicationService mCurrentVrService; private Context mContext; private ComponentName mCurrentVrModeComponent; private int mCurrentVrModeUser; + private boolean mWasDefaultGranted; + private boolean mGuard; + private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>(); + private String mPreviousNotificationPolicyAccessPackage; + private String mPreviousManageOverlayPackage; private static final BinderChecker sBinderChecker = new BinderChecker() { @Override @@ -239,62 +253,271 @@ public class VrManagerService extends SystemService implements EnabledComponentC * * @return {@code true} if the component/user combination specified is valid. */ - private boolean updateCurrentVrServiceLocked(boolean enabled, - @NonNull ComponentName component, int userId, ComponentName calling) { + private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component, + int userId, ComponentName calling) { boolean sendUpdatedCaller = false; + final long identity = Binder.clearCallingIdentity(); + try { - boolean validUserComponent = (mComponentObserver.isValid(component, userId) == - EnabledComponentsObserver.NO_ERROR); + boolean validUserComponent = (mComponentObserver.isValid(component, userId) == + EnabledComponentsObserver.NO_ERROR); - // Always send mode change events. - changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null); + // Always send mode change events. + changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null); - if (!enabled || !validUserComponent) { - // Unbind whatever is running - if (mCurrentVrService != null) { - Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " + - mCurrentVrService.getUserId()); - mCurrentVrService.disconnect(); - mCurrentVrService = null; - } - } else { - if (mCurrentVrService != null) { - // Unbind any running service that doesn't match the component/user selection - if (mCurrentVrService.disconnectIfNotMatching(component, userId)) { + if (!enabled || !validUserComponent) { + // Unbind whatever is running + if (mCurrentVrService != null) { Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " + - mCurrentVrService.getUserId()); + mCurrentVrService.getUserId()); + mCurrentVrService.disconnect(); + disableImpliedPermissionsLocked(mCurrentVrService.getComponent(), + new UserHandle(mCurrentVrService.getUserId())); + mCurrentVrService = null; + } + } else { + if (mCurrentVrService != null) { + // Unbind any running service that doesn't match the component/user selection + if (mCurrentVrService.disconnectIfNotMatching(component, userId)) { + Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + + " for user " + mCurrentVrService.getUserId()); + disableImpliedPermissionsLocked(mCurrentVrService.getComponent(), + new UserHandle(mCurrentVrService.getUserId())); + createAndConnectService(component, userId); + enableImpliedPermissionsLocked(mCurrentVrService.getComponent(), + new UserHandle(mCurrentVrService.getUserId())); + sendUpdatedCaller = true; + } + // The service with the correct component/user is bound + } else { + // Nothing was previously running, bind a new service createAndConnectService(component, userId); + enableImpliedPermissionsLocked(mCurrentVrService.getComponent(), + new UserHandle(mCurrentVrService.getUserId())); sendUpdatedCaller = true; } - // The service with the correct component/user is bound - } else { - // Nothing was previously running, bind a new service - createAndConnectService(component, userId); + } + + if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) { + mCurrentVrModeComponent = calling; + mCurrentVrModeUser = userId; sendUpdatedCaller = true; } + + if (mCurrentVrService != null && sendUpdatedCaller) { + final ComponentName c = mCurrentVrModeComponent; + mCurrentVrService.sendEvent(new PendingEvent() { + @Override + public void runEvent(IInterface service) throws RemoteException { + IVrListener l = (IVrListener) service; + l.focusedActivityChanged(c); + } + }); + } + + return validUserComponent; + } finally { + Binder.restoreCallingIdentity(identity); } + } - if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) { - mCurrentVrModeComponent = calling; - mCurrentVrModeUser = userId; - sendUpdatedCaller = true; + /** + * Enable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given + * component package and user. + * + * @param component the component whose package should be enabled. + * @param userId the user that owns the given component. + */ + private void enableImpliedPermissionsLocked(ComponentName component, UserHandle userId) { + if (mGuard) { + // Impossible + throw new IllegalStateException("Enabling permissions without disabling."); } + mGuard = true; - if (mCurrentVrService != null && sendUpdatedCaller) { - final ComponentName c = mCurrentVrModeComponent; - mCurrentVrService.sendEvent(new PendingEvent() { - @Override - public void runEvent(IInterface service) throws RemoteException { - IVrListener l = (IVrListener) service; - l.focusedActivityChanged(c); - } - }); + PackageManager pm = mContext.getPackageManager(); + + String pName = component.getPackageName(); + if (pm == null) { + Slog.e(TAG, "Couldn't set implied permissions for " + pName + + ", PackageManager isn't running"); + return; } - return validUserComponent; + ApplicationInfo info = null; + try { + info = pm.getApplicationInfo(pName, PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) { + } + + if (info == null) { + Slog.e(TAG, "Couldn't set implied permissions for " + pName + ", no such package."); + return; + } + + if (!(info.isSystemApp() || info.isUpdatedSystemApp())) { + return; // Application is not pre-installed, avoid setting implied permissions + } + + mWasDefaultGranted = true; + + grantOverlayAccess(pName, userId); + grantNotificationPolicyAccess(pName); + grantNotificationListenerAccess(pName, userId); } + /** + * Disable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given + * component package and user. + * + * @param component the component whose package should be disabled. + * @param userId the user that owns the given component. + */ + private void disableImpliedPermissionsLocked(ComponentName component, UserHandle userId) { + if (!mGuard) { + // Impossible + throw new IllegalStateException("Disabling permissions without enabling."); + } + mGuard = false; + + PackageManager pm = mContext.getPackageManager(); + + if (pm == null) { + Slog.e(TAG, "Couldn't remove implied permissions for " + component + + ", PackageManager isn't running"); + return; + } + + String pName = component.getPackageName(); + if (mWasDefaultGranted) { + revokeOverlayAccess(userId); + revokeNotificationPolicyAccess(pName); + revokeNotificiationListenerAccess(); + mWasDefaultGranted = false; + } + + } + + private void grantOverlayAccess(String pkg, UserHandle userId) { + PackageManager pm = mContext.getPackageManager(); + boolean prev = (PackageManager.PERMISSION_GRANTED == + pm.checkPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, pkg)); + mPreviousManageOverlayPackage = null; + if (!prev) { + pm.grantRuntimePermission(pkg, android.Manifest.permission.SYSTEM_ALERT_WINDOW, + userId); + mPreviousManageOverlayPackage = pkg; + } + } + + private void revokeOverlayAccess(UserHandle userId) { + PackageManager pm = mContext.getPackageManager(); + if (mPreviousManageOverlayPackage != null) { + pm.revokeRuntimePermission(mPreviousManageOverlayPackage, + android.Manifest.permission.SYSTEM_ALERT_WINDOW, userId); + mPreviousManageOverlayPackage = null; + } + } + + + private void grantNotificationPolicyAccess(String pkg) { + NotificationManager nm = mContext.getSystemService(NotificationManager.class); + boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg); + mPreviousNotificationPolicyAccessPackage = null; + if (!prev) { + mPreviousNotificationPolicyAccessPackage = pkg; + nm.setNotificationPolicyAccessGranted(pkg, true); + } + } + + private void revokeNotificationPolicyAccess(String pkg) { + NotificationManager nm = mContext.getSystemService(NotificationManager.class); + if (mPreviousNotificationPolicyAccessPackage != null) { + nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false); + mPreviousNotificationPolicyAccessPackage = null; + } + } + + private void grantNotificationListenerAccess(String pkg, UserHandle userId) { + PackageManager pm = mContext.getPackageManager(); + ArraySet<ComponentName> possibleServices = EnabledComponentsObserver.loadComponentNames(pm, + userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE, + android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE); + ContentResolver resolver = mContext.getContentResolver(); + + ArraySet<String> current = getCurrentNotifListeners(resolver); + + mPreviousToggledListenerSettings.clear(); + + for (ComponentName c : possibleServices) { + String flatName = c.flattenToString(); + if (Objects.equals(c.getPackageName(), pkg) + && !current.contains(flatName)) { + mPreviousToggledListenerSettings.add(flatName); + current.add(flatName); + } + } + + if (current.size() > 0) { + String flatSettings = formatSettings(current); + Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, + flatSettings); + } + } + + private void revokeNotificiationListenerAccess() { + if (mPreviousToggledListenerSettings.isEmpty()) { + return; + } + + ContentResolver resolver = mContext.getContentResolver(); + ArraySet<String> current = getCurrentNotifListeners(resolver); + + current.removeAll(mPreviousToggledListenerSettings); + mPreviousToggledListenerSettings.clear(); + + String flatSettings = formatSettings(current); + Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, + flatSettings); + } + + private ArraySet<String> getCurrentNotifListeners(ContentResolver resolver) { + String flat = Settings.Secure.getString(resolver, + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + + ArraySet<String> current = new ArraySet<>(); + if (flat != null) { + String[] allowed = flat.split(":"); + for (String s : allowed) { + current.add(s); + } + } + return current; + } + + private static String formatSettings(Collection<String> c) { + if (c == null || c.isEmpty()) { + return ""; + } + + StringBuilder b = new StringBuilder(); + boolean start = true; + for (String s : c) { + if ("".equals(s)) { + continue; + } + if (!start) { + b.append(':'); + } + b.append(s); + start = false; + } + return b.toString(); + } + + + private void createAndConnectService(@NonNull ComponentName component, int userId) { mCurrentVrService = VrManagerService.create(mContext, component, userId); mCurrentVrService.connect(); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 1c5556f23811..fb3c6ec65b8a 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1285,6 +1285,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.imageWallpaperPending = false; if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { wallpaper.wallpaperId = makeWallpaperIdLocked(); + notifyCallbacksLocked(wallpaper); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index cf27b9722de3..aace5e7136ca 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -589,7 +589,7 @@ class DragState { void overridePointerIconLw(int touchSource) { mTouchSource = touchSource; if (isFromSource(InputDevice.SOURCE_MOUSE)) { - InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRAB); + InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRABBING); } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1f03c041b477..eea0ca0666f7 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -425,7 +425,7 @@ class Task implements DimLayer.DimLayerUser { if (mFullscreen || !StackId.isTaskResizeableByDockedStack(mStack.mStackId) || displayContent == null - || displayContent.getDockedStackLocked() != null) { + || displayContent.getDockedStackVisibleForUserLocked() != null) { return true; } return false; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 0bf7102ddeb8..bb0ec4c33049 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -128,6 +128,10 @@ public class TaskStack implements DimLayer.DimLayerUser, // in which case a second window animation would cause jitter. private boolean mFreezeMovementAnimations = false; + // Temporary storage for the new bounds that should be used after the configuration change. + // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration(). + private final Rect mBoundsAfterRotation = new Rect(); + TaskStack(WindowManagerService service, int stackId) { mService = service; mStackId = stackId; @@ -343,28 +347,28 @@ public class TaskStack implements DimLayer.DimLayerUser, setBounds(mTmpRect2); } else { mLastUpdateDisplayInfoRotation = newRotation; - updateBoundsAfterRotation(); + updateBoundsAfterRotation(true); } } - void onConfigurationChanged() { + boolean onConfigurationChanged() { mLastConfigChangedRotation = getDisplayInfo().rotation; - updateBoundsAfterRotation(); + return updateBoundsAfterRotation(false); } - void updateBoundsAfterRotation() { + boolean updateBoundsAfterRotation(boolean scheduleResize) { if (mLastConfigChangedRotation != mLastUpdateDisplayInfoRotation) { // We wait for the rotation values after configuration change and display info. update // to be equal before updating the bounds due to rotation change otherwise things might // get out of alignment... - return; + return false; } final int newRotation = getDisplayInfo().rotation; if (mRotation == newRotation) { // Nothing to do here if the rotation didn't change - return; + return false; } mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2); @@ -373,11 +377,22 @@ public class TaskStack implements DimLayer.DimLayerUser, snapDockedStackAfterRotation(mTmpRect2); } - // Post message to inform activity manager of the bounds change simulating - // a one-way call. We do this to prevent a deadlock between window manager - // lock and activity manager lock been held. - mService.mH.obtainMessage( - RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget(); + if (scheduleResize) { + // Post message to inform activity manager of the bounds change simulating + // a one-way call. We do this to prevent a deadlock between window manager + // lock and activity manager lock been held. + mService.mH.obtainMessage(RESIZE_STACK, mStackId, + 0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget(); + } else { + mBoundsAfterRotation.set(mTmpRect2); + } + + return true; + } + + void getBoundsForNewConfiguration(Rect outBounds) { + outBounds.set(mBoundsAfterRotation); + mBoundsAfterRotation.setEmpty(); } /** @@ -869,6 +884,10 @@ public class TaskStack implements DimLayer.DimLayerUser, } } + boolean isAdjustedForMinimizedDock() { + return mMinimizeAmount != 0f; + } + private boolean adjustForIME(final WindowState imeWin) { final int dockedSide = getDockSide(); final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index dcb4a63a560a..14ae74f3967d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -125,6 +125,7 @@ import com.android.internal.R; import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IShortcutService; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -515,6 +516,8 @@ public class WindowManagerService extends IWindowManager.Stub private final SparseIntArray mTmpTaskIds = new SparseIntArray(); + private final ArrayList<Integer> mChangedStackList = new ArrayList(); + boolean mForceResizableTasks = false; int getDragLayerLocked() { @@ -3398,7 +3401,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (isStackVisibleLocked(DOCKED_STACK_ID) + if ((isStackVisibleLocked(DOCKED_STACK_ID) + && !mStackIdToStack.get(DOCKED_STACK_ID).isAdjustedForMinimizedDock()) || isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) { // We don't let app affect the system orientation when in freeform or docked mode since // they don't occupy the entire display and their request can conflict with other apps. @@ -3580,7 +3584,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setNewConfiguration(Configuration config) { + public int[] setNewConfiguration(Configuration config) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setNewConfiguration()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3592,16 +3596,30 @@ public class WindowManagerService extends IWindowManager.Stub mWaitingForConfig = false; mLastFinishedFreezeSource = "new-config"; } - onConfigurationChanged(); - mWindowPlacerLocked.performSurfacePlacement(); + return onConfigurationChanged(); } } - private void onConfigurationChanged() { + @Override + public Rect getBoundsForNewConfiguration(int stackId) { + synchronized(mWindowMap) { + final TaskStack stack = mStackIdToStack.get(stackId); + final Rect outBounds = new Rect(); + stack.getBoundsForNewConfiguration(outBounds); + return outBounds; + } + } + + private int[] onConfigurationChanged() { + mChangedStackList.clear(); for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) { final TaskStack stack = mStackIdToStack.valueAt(stackNdx); - stack.onConfigurationChanged(); + if (stack.onConfigurationChanged()) { + mChangedStackList.add(stack.mStackId); + } } + return mChangedStackList.isEmpty() ? + null : ArrayUtils.convertToIntArray(mChangedStackList); } @Override diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0f5930244f6e..659450efcce3 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -169,7 +169,7 @@ public final class SystemServer { * visual content. */ private static final int DEFAULT_SYSTEM_THEME = - com.android.internal.R.style.Theme_Material_DayNight_DarkActionBar; + com.android.internal.R.style.Theme_Material_Light_DarkActionBar; private final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index e27f69efbe83..a8814088d489 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -57,6 +57,17 @@ abstract class DhcpPacket { public static final int HWADDR_LEN = 16; public static final int MAX_OPTION_LEN = 255; + + /** + * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum + * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280, + * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500 + * because in general it is risky to assume that the hardware is able to send/receive packets + * larger than 1500 bytes even if the network supports it. + */ + private static final int MIN_MTU = 1280; + private static final int MAX_MTU = 1500; + /** * IP layer definitions. */ @@ -917,7 +928,7 @@ abstract class DhcpPacket { break; case DHCP_MTU: expectedLen = 2; - mtu = Short.valueOf(packet.getShort()); + mtu = packet.getShort(); break; case DHCP_DOMAIN_NAME: expectedLen = optionLen; @@ -1106,6 +1117,8 @@ abstract class DhcpPacket { results.serverAddress = mServerIdentifier; results.vendorInfo = mVendorInfo; results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE; + results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0; + return results; } diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index c25fae306c39..d10834ad9695 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -605,6 +605,10 @@ public class IpManager extends StateMachine { } } newLp.setDomains(mDhcpResults.domains); + + if (mDhcpResults.mtu != 0) { + newLp.setMtu(mDhcpResults.mtu); + } } // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java index 876d95b7fa50..f8eaf7d0ee26 100644 --- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java +++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java @@ -261,7 +261,7 @@ public class DhcpPacketTest extends TestCase { private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString, String domains, String serverAddress, String vendorInfo, int leaseDuration, - boolean hasMeteredHint, DhcpResults dhcpResults) throws Exception { + boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception { assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress); assertEquals(v4Address(gateway), dhcpResults.gateway); @@ -277,6 +277,7 @@ public class DhcpPacketTest extends TestCase { assertEquals(vendorInfo, dhcpResults.vendorInfo); assertEquals(leaseDuration, dhcpResults.leaseDuration); assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint()); + assertEquals(mtu, dhcpResults.mtu); } @SmallTest @@ -310,7 +311,7 @@ public class DhcpPacketTest extends TestCase { assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4", - null, "192.168.144.3", null, 7200, false, dhcpResults); + null, "192.168.144.3", null, 7200, false, 0, dhcpResults); } @SmallTest @@ -342,10 +343,70 @@ public class DhcpPacketTest extends TestCase { assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1", - null, "192.168.43.1", "ANDROID_METERED", 3600, true, dhcpResults); + null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults); assertTrue(dhcpResults.hasMeteredHint()); } + private byte[] mtuBytes(int mtu) { + // 0x1a02: option 26, length 2. 0xff: no more options. + if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) { + throw new IllegalArgumentException( + String.format("Invalid MTU %d, must be 16-bit unsigned", mtu)); + } + String hexString = String.format("1a02%04xff", mtu); + return HexEncoding.decode(hexString.toCharArray(), false); + } + + private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception { + if (mtuBytes != null) { + packet.position(packet.capacity() - mtuBytes.length); + packet.put(mtuBytes); + packet.clear(); + } + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); + assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4", + null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults); + } + + @SmallTest + public void testMtu() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // IP header. + "451001480000000080118849c0a89003c0a89ff7" + + // UDP header. + "004300440134dcfa" + + // BOOTP header. + "02010600c997a63b0000000000000000c0a89ff70000000000000000" + + // MAC address. + "30766ff2a90c00000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options + "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" + + "3a0400000e103b040000189cff00000000" + ).toCharArray(), false)); + + checkMtu(packet, 0, null); + checkMtu(packet, 0, mtuBytes(1501)); + checkMtu(packet, 1500, mtuBytes(1500)); + checkMtu(packet, 1499, mtuBytes(1499)); + checkMtu(packet, 1280, mtuBytes(1280)); + checkMtu(packet, 0, mtuBytes(1279)); + checkMtu(packet, 0, mtuBytes(576)); + checkMtu(packet, 0, mtuBytes(68)); + checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE)); + checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3)); + checkMtu(packet, 0, mtuBytes(-1)); + } + @SmallTest public void testBadHwaddrLength() throws Exception { final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( @@ -453,7 +514,7 @@ public class DhcpPacketTest extends TestCase { assertTrue(offerPacket instanceof DhcpOfferPacket); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1", - null, "1.1.1.1", null, 43200, false, dhcpResults); + null, "1.1.1.1", null, 43200, false, 0, dhcpResults); } @SmallTest @@ -484,7 +545,7 @@ public class DhcpPacketTest extends TestCase { assertTrue(offerPacket instanceof DhcpOfferPacket); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2", - "domain123.co.uk", "192.0.2.254", null, 49094, false, dhcpResults); + "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults); } @SmallTest @@ -518,7 +579,7 @@ public class DhcpPacketTest extends TestCase { assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac())); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53", - "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults); + "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults); } @SmallTest @@ -554,7 +615,7 @@ public class DhcpPacketTest extends TestCase { DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("10.15.122.242/16", "10.15.200.23", "209.129.128.3,209.129.148.3,209.129.128.6", - "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults); + "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults); } @SmallTest @@ -621,7 +682,7 @@ public class DhcpPacketTest extends TestCase { assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac())); DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4", - null, "192.171.189.2", null, 28800, false, dhcpResults); + null, "192.171.189.2", null, 28800, false, 0, dhcpResults); } @SmallTest diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java new file mode 100644 index 000000000000..83a59fd85d29 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.notification; + + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.Notification.Builder; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Handler; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.Vibrator; +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.StatusBarNotification; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class BuzzBeepBlinkTest extends AndroidTestCase { + + @Mock AudioManager mAudioManager; + @Mock Vibrator mVibrator; + @Mock android.media.IRingtonePlayer mRingtonePlayer; + @Mock Handler mHandler; + + private NotificationManagerService mService; + private String mPkg = "com.android.server.notification"; + private int mId = 1001; + private int mOtherId = 1002; + private String mTag = null; + private int mUid = 1000; + private int mPid = 2000; + private int mScore = 10; + private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); + + @Override + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mAudioManager.isAudioFocusExclusive()).thenReturn(false); + when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer); + when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10); + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); + + mService = new NotificationManagerService(getContext()); + mService.setAudioManager(mAudioManager); + mService.setVibrator(mVibrator); + mService.setSystemReady(true); + mService.setHandler(mHandler); + } + + // + // Convenience functions for creating notification records + // + + private NotificationRecord getNoisyOtherNotification() { + return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, + true /* noisy */, true /* buzzy*/); + } + + private NotificationRecord getBeepyNotification() { + return getNotificationRecord(mId, false /* insistent */, false /* once */, + true /* noisy */, false /* buzzy*/); + } + + private NotificationRecord getBeepyOnceNotification() { + return getNotificationRecord(mId, false /* insistent */, true /* once */, + true /* noisy */, false /* buzzy*/); + } + + private NotificationRecord getQuietNotification() { + return getNotificationRecord(mId, false /* insistent */, false /* once */, + false /* noisy */, false /* buzzy*/); + } + + private NotificationRecord getQuietOtherNotification() { + return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, + false /* noisy */, false /* buzzy*/); + } + + private NotificationRecord getQuietOnceNotification() { + return getNotificationRecord(mId, false /* insistent */, true /* once */, + false /* noisy */, false /* buzzy*/); + } + + private NotificationRecord getInsistentBeepyNotification() { + return getNotificationRecord(mId, true /* insistent */, false /* once */, + true /* noisy */, false /* buzzy*/); + } + + private NotificationRecord getBuzzyNotification() { + return getNotificationRecord(mId, false /* insistent */, false /* once */, + false /* noisy */, true /* buzzy*/); + } + + private NotificationRecord getBuzzyOnceNotification() { + return getNotificationRecord(mId, false /* insistent */, true /* once */, + false /* noisy */, true /* buzzy*/); + } + + private NotificationRecord getInsistentBuzzyNotification() { + return getNotificationRecord(mId, true /* insistent */, false /* once */, + false /* noisy */, true /* buzzy*/); + } + + private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, + boolean noisy, boolean buzzy) { + final Builder builder = new Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setPriority(Notification.PRIORITY_HIGH) + .setOnlyAlertOnce(once); + + int defaults = 0; + if (noisy) { + defaults |= Notification.DEFAULT_SOUND; + } + if (buzzy) { + defaults |= Notification.DEFAULT_VIBRATE; + } + builder.setDefaults(defaults); + + Notification n = builder.build(); + if (insistent) { + n.flags |= Notification.FLAG_INSISTENT; + } + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, + mScore, n, mUser, System.currentTimeMillis()); + return new NotificationRecord(getContext(), sbn); + } + + // + // Convenience functions for interacting with mocks + // + + private void verifyNeverBeep() throws RemoteException { + verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(), + anyBoolean(), (AudioAttributes) anyObject()); + } + + private void verifyBeep() throws RemoteException { + verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), + eq(true), (AudioAttributes) anyObject()); + } + + private void verifyBeepLooped() throws RemoteException { + verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), + eq(false), (AudioAttributes) anyObject()); + } + + private void verifyNeverStopAudio() throws RemoteException { + verify(mRingtonePlayer, never()).stopAsync(); + } + + private void verifyStopAudio() throws RemoteException { + verify(mRingtonePlayer, times(1)).stopAsync(); + } + + private void verifyNeverVibrate() { + verify(mVibrator, never()).vibrate(anyInt(), anyString(), (long[]) anyObject(), + anyInt(), (AudioAttributes) anyObject()); + } + + private void verifyVibrate() { + verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(), + eq(-1), (AudioAttributes) anyObject()); + } + + private void verifyVibrateLooped() { + verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(), + eq(0), (AudioAttributes) anyObject()); + } + + private void verifyStopVibrate() { + verify(mVibrator, times(1)).cancel(); + } + + private void verifyNeverStopVibrate() throws RemoteException { + verify(mVibrator, never()).cancel(); + } + + @SmallTest + public void testBeep() throws Exception { + NotificationRecord r = getBeepyNotification(); + + mService.buzzBeepBlinkLocked(r); + + verifyBeepLooped(); + verifyNeverVibrate(); + } + + // + // Tests + // + + @SmallTest + public void testBeepInsistently() throws Exception { + NotificationRecord r = getInsistentBeepyNotification(); + + mService.buzzBeepBlinkLocked(r); + + verifyBeep(); + } + + @SmallTest + public void testNoInterruptionForMin() throws Exception { + NotificationRecord r = getBeepyNotification(); + r.setImportance(Ranking.IMPORTANCE_MIN, "foo"); + + mService.buzzBeepBlinkLocked(r); + + verifyNeverBeep(); + verifyNeverVibrate(); + } + + @SmallTest + public void testNoInterruptionForIntercepted() throws Exception { + NotificationRecord r = getBeepyNotification(); + r.setIntercepted(true); + + mService.buzzBeepBlinkLocked(r); + + verifyNeverBeep(); + verifyNeverVibrate(); + } + + @SmallTest + public void testBeepTwice() throws Exception { + NotificationRecord r = getBeepyNotification(); + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mRingtonePlayer); + + // update should beep + r.isUpdate = true; + mService.buzzBeepBlinkLocked(r); + verifyBeepLooped(); + } + + @SmallTest + public void testHonorAlertOnlyOnceForBeep() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord s = getBeepyOnceNotification(); + s.isUpdate = true; + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mRingtonePlayer); + + // update should not beep + mService.buzzBeepBlinkLocked(s); + verifyNeverBeep(); + } + + @SmallTest + public void testNoisyUpdateDoesNotCancelAudio() throws Exception { + NotificationRecord r = getBeepyNotification(); + + mService.buzzBeepBlinkLocked(r); + r.isUpdate = true; + mService.buzzBeepBlinkLocked(r); + + verifyNeverStopAudio(); + } + + @SmallTest + public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord s = getBeepyOnceNotification(); + s.isUpdate = true; + + mService.buzzBeepBlinkLocked(r); + mService.buzzBeepBlinkLocked(s); + + verifyNeverStopAudio(); + } + + @SmallTest + public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord s = getQuietNotification(); + s.isUpdate = true; + NotificationRecord other = getNoisyOtherNotification(); + + // set up internal state + mService.buzzBeepBlinkLocked(r); + mService.buzzBeepBlinkLocked(other); // this takes the audio stream + Mockito.reset(mRingtonePlayer); + + // should not stop noise, since we no longer own it + mService.buzzBeepBlinkLocked(s); // this no longer owns the stream + verifyNeverStopAudio(); + } + + @SmallTest + public void testQuietInterloperDoesNotCancelAudio() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord other = getQuietOtherNotification(); + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mRingtonePlayer); + + // should not stop noise, since it does not own it + mService.buzzBeepBlinkLocked(other); + verifyNeverStopAudio(); + } + + @SmallTest + public void testQuietUpdateCancelsAudio() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord s = getQuietNotification(); + s.isUpdate = true; + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mRingtonePlayer); + + // quiet update should stop making noise + mService.buzzBeepBlinkLocked(s); + verifyStopAudio(); + } + + @SmallTest + public void testQuietOnceUpdateCancelsAudio() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord s = getQuietOnceNotification(); + s.isUpdate = true; + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mRingtonePlayer); + + // stop making noise - this is a weird corner case, but quiet should override once + mService.buzzBeepBlinkLocked(s); + verifyStopAudio(); + } + + @SmallTest + public void testDemoteSoundToVibrate() throws Exception { + NotificationRecord r = getBeepyNotification(); + + // the phone is quiet + when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); + + mService.buzzBeepBlinkLocked(r); + + verifyNeverBeep(); + verifyVibrate(); + } + + @SmallTest + public void testDemotInsistenteSoundToVibrate() throws Exception { + NotificationRecord r = getInsistentBeepyNotification(); + + // the phone is quiet + when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); + + mService.buzzBeepBlinkLocked(r); + + verifyVibrateLooped(); + } + + @SmallTest + public void testVibrate() throws Exception { + NotificationRecord r = getBuzzyNotification(); + + mService.buzzBeepBlinkLocked(r); + + verifyNeverBeep(); + verifyVibrate(); + } + + @SmallTest + public void testInsistenteVibrate() throws Exception { + NotificationRecord r = getInsistentBuzzyNotification(); + + mService.buzzBeepBlinkLocked(r); + verifyVibrateLooped(); + } + + @SmallTest + public void testVibratTwice() throws Exception { + NotificationRecord r = getBuzzyNotification(); + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mVibrator); + + // update should vibrate + r.isUpdate = true; + mService.buzzBeepBlinkLocked(r); + verifyVibrate(); + } + + @SmallTest + public void testHonorAlertOnlyOnceForBuzz() throws Exception { + NotificationRecord r = getBuzzyNotification(); + NotificationRecord s = getBuzzyOnceNotification(); + s.isUpdate = true; + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mVibrator); + + // update should not beep + mService.buzzBeepBlinkLocked(s); + verifyNeverVibrate(); + } + + @SmallTest + public void testNoisyUpdateDoesNotCancelVibrate() throws Exception { + NotificationRecord r = getBuzzyNotification(); + + mService.buzzBeepBlinkLocked(r); + r.isUpdate = true; + mService.buzzBeepBlinkLocked(r); + + verifyNeverStopVibrate(); + } + + @SmallTest + public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception { + NotificationRecord r = getBuzzyNotification(); + NotificationRecord s = getBuzzyOnceNotification(); + s.isUpdate = true; + + mService.buzzBeepBlinkLocked(r); + mService.buzzBeepBlinkLocked(s); + + verifyNeverStopVibrate(); + } + + @SmallTest + public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception { + NotificationRecord r = getBuzzyNotification(); + NotificationRecord s = getQuietNotification(); + s.isUpdate = true; + NotificationRecord other = getNoisyOtherNotification(); + + // set up internal state + mService.buzzBeepBlinkLocked(r); + mService.buzzBeepBlinkLocked(other); // this takes the vibrate stream + Mockito.reset(mVibrator); + + // should not stop vibrate, since we no longer own it + mService.buzzBeepBlinkLocked(s); // this no longer owns the stream + verifyNeverStopVibrate(); + } + + @SmallTest + public void testQuietInterloperDoesNotCancelVibrate() throws Exception { + NotificationRecord r = getBuzzyNotification(); + NotificationRecord other = getQuietOtherNotification(); + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mVibrator); + + // should not stop noise, since it does not own it + mService.buzzBeepBlinkLocked(other); + verifyNeverStopVibrate(); + } + + @SmallTest + public void testQuietUpdateCancelsVibrate() throws Exception { + NotificationRecord r = getBuzzyNotification(); + NotificationRecord s = getQuietNotification(); + s.isUpdate = true; + + // set up internal state + mService.buzzBeepBlinkLocked(r); + + // quiet update should stop making noise + mService.buzzBeepBlinkLocked(s); + verifyStopVibrate(); + } + + @SmallTest + public void testQuietOnceUpdateCancelsvibrate() throws Exception { + NotificationRecord r = getBuzzyNotification(); + NotificationRecord s = getQuietOnceNotification(); + s.isUpdate = true; + + // set up internal state + mService.buzzBeepBlinkLocked(r); + Mockito.reset(mVibrator); + + // stop making noise - this is a weird corner case, but quiet should override once + mService.buzzBeepBlinkLocked(s); + verifyStopVibrate(); + } + + @SmallTest + public void testQuietUpdateCancelsDemotedVibrate() throws Exception { + NotificationRecord r = getBeepyNotification(); + NotificationRecord s = getQuietNotification(); + + // the phone is quiet + when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); + + mService.buzzBeepBlinkLocked(r); + + // quiet update should stop making noise + mService.buzzBeepBlinkLocked(s); + verifyStopVibrate(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java deleted file mode 100644 index c44ffa481f8b..000000000000 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java +++ /dev/null @@ -1,271 +0,0 @@ - -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.pm; - -import android.content.ComponentName; -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.graphics.drawable.Icon; -import android.os.Bundle; -import android.os.Parcel; -import android.os.PersistableBundle; -import android.test.AndroidTestCase; - -import com.android.internal.util.Preconditions; -import com.android.server.testutis.TestUtils; - -/** - * Tests for {@link ShortcutInfo}. - - m FrameworksServicesTests && - adb install \ - -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && - adb shell am instrument -e class com.android.server.pm.ShortcutInfoTest \ - -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner - - */ -public class ShortcutInfoTest extends AndroidTestCase { - - public void testMissingMandatoryFields() { - TestUtils.assertExpectException( - IllegalArgumentException.class, - "ID must be provided", - () -> new ShortcutInfo.Builder(mContext).build()); - TestUtils.assertExpectException( - IllegalArgumentException.class, - "title must be provided", - () -> new ShortcutInfo.Builder(mContext).setId("id").build() - .enforceMandatoryFields()); - TestUtils.assertExpectException( - NullPointerException.class, - "Intent must be provided", - () -> new ShortcutInfo.Builder(mContext).setId("id").setTitle("x").build() - .enforceMandatoryFields()); - } - - private ShortcutInfo parceled(ShortcutInfo si) { - Parcel p = Parcel.obtain(); - p.writeParcelable(si, 0); - p.setDataPosition(0); - ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader()); - p.recycle(); - return si2; - } - - private Intent makeIntent(String action, Object... bundleKeysAndValues) { - final Intent intent = new Intent(action); - intent.replaceExtras(ShortcutManagerTest.makeBundle(bundleKeysAndValues)); - return intent; - } - - public void testParcel() { - ShortcutInfo si = parceled(new ShortcutInfo.Builder(getContext()) - .setId("id") - .setTitle("title") - .setIntent(makeIntent("action")) - .build()); - assertEquals(getContext().getPackageName(), si.getPackageName()); - assertEquals("id", si.getId()); - assertEquals("title", si.getTitle()); - assertEquals("action", si.getIntent().getAction()); - - PersistableBundle pb = new PersistableBundle(); - pb.putInt("k", 1); - - si = new ShortcutInfo.Builder(getContext()) - .setId("id") - .setActivityComponent(new ComponentName("a", "b")) - .setIcon(Icon.createWithContentUri("content://a.b.c/")) - .setTitle("title") - .setText("text") - .setIntent(makeIntent("action", "key", "val")) - .setWeight(123) - .setExtras(pb) - .build(); - si.addFlags(ShortcutInfo.FLAG_PINNED); - si.setBitmapPath("abc"); - si.setIconResourceId(456); - - si = parceled(si); - - assertEquals(getContext().getPackageName(), si.getPackageName()); - assertEquals("id", si.getId()); - assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); - assertEquals("content://a.b.c/", si.getIcon().getUriString()); - assertEquals("title", si.getTitle()); - assertEquals("text", si.getText()); - assertEquals("action", si.getIntent().getAction()); - assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); - assertEquals(1, si.getExtras().getInt("k")); - - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); - assertEquals("abc", si.getBitmapPath()); - assertEquals(456, si.getIconResourceId()); - } - - public void testClone() { - PersistableBundle pb = new PersistableBundle(); - pb.putInt("k", 1); - ShortcutInfo sorig = new ShortcutInfo.Builder(getContext()) - .setId("id") - .setActivityComponent(new ComponentName("a", "b")) - .setIcon(Icon.createWithContentUri("content://a.b.c/")) - .setTitle("title") - .setText("text") - .setIntent(makeIntent("action", "key", "val")) - .setWeight(123) - .setExtras(pb) - .build(); - sorig.addFlags(ShortcutInfo.FLAG_PINNED); - sorig.setBitmapPath("abc"); - sorig.setIconResourceId(456); - - ShortcutInfo si = sorig.clone(/* clone flags*/ 0); - - assertEquals(getContext().getPackageName(), si.getPackageName()); - assertEquals("id", si.getId()); - assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); - assertEquals("content://a.b.c/", si.getIcon().getUriString()); - assertEquals("title", si.getTitle()); - assertEquals("text", si.getText()); - assertEquals("action", si.getIntent().getAction()); - assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); - assertEquals(1, si.getExtras().getInt("k")); - - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); - assertEquals("abc", si.getBitmapPath()); - assertEquals(456, si.getIconResourceId()); - - si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); - - assertEquals(getContext().getPackageName(), si.getPackageName()); - assertEquals("id", si.getId()); - assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); - assertEquals(null, si.getIcon()); - assertEquals("title", si.getTitle()); - assertEquals("text", si.getText()); - assertEquals("action", si.getIntent().getAction()); - assertEquals("val", si.getIntent().getStringExtra("key")); - assertEquals(123, si.getWeight()); - assertEquals(1, si.getExtras().getInt("k")); - - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); - assertEquals(null, si.getBitmapPath()); - assertEquals(0, si.getIconResourceId()); - - si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); - - assertEquals(getContext().getPackageName(), si.getPackageName()); - assertEquals("id", si.getId()); - assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); - assertEquals(null, si.getIcon()); - assertEquals("title", si.getTitle()); - assertEquals("text", si.getText()); - assertEquals(null, si.getIntent()); - assertEquals(123, si.getWeight()); - assertEquals(1, si.getExtras().getInt("k")); - - assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); - assertEquals(null, si.getBitmapPath()); - assertEquals(0, si.getIconResourceId()); - - si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); - - assertEquals(getContext().getPackageName(), si.getPackageName()); - assertEquals("id", si.getId()); - assertEquals(null, si.getActivityComponent()); - assertEquals(null, si.getIcon()); - assertEquals(null, si.getTitle()); - assertEquals(null, si.getText()); - assertEquals(null, si.getIntent()); - assertEquals(0, si.getWeight()); - assertEquals(null, si.getExtras()); - - assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); - assertEquals(null, si.getBitmapPath()); - assertEquals(0, si.getIconResourceId()); - } - - - public void testCopyNonNullFieldsFrom() { - PersistableBundle pb = new PersistableBundle(); - pb.putInt("k", 1); - ShortcutInfo sorig = new ShortcutInfo.Builder(getContext()) - .setId("id") - .setActivityComponent(new ComponentName("a", "b")) - .setIcon(Icon.createWithContentUri("content://a.b.c/")) - .setTitle("title") - .setText("text") - .setIntent(makeIntent("action", "key", "val")) - .setWeight(123) - .setExtras(pb) - .build(); - sorig.addFlags(ShortcutInfo.FLAG_PINNED); - sorig.setBitmapPath("abc"); - sorig.setIconResourceId(456); - - ShortcutInfo si; - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setActivityComponent(new ComponentName("x", "y")).build()); - assertEquals(new ComponentName("x", "y"), si.getActivityComponent()); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setIcon(Icon.createWithContentUri("content://x.y.z/")).build()); - assertEquals("content://x.y.z/", si.getIcon().getUriString()); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setTitle("xyz").build()); - assertEquals("xyz", si.getTitle()); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setText("xxx").build()); - assertEquals("xxx", si.getText()); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setIntent(makeIntent("action2")).build()); - assertEquals("action2", si.getIntent().getAction()); - assertEquals(null, si.getIntent().getStringExtra("key")); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setIntent(makeIntent("action3", "key", "x")).build()); - assertEquals("action3", si.getIntent().getAction()); - assertEquals("x", si.getIntent().getStringExtra("key")); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setWeight(999).build()); - assertEquals(999, si.getWeight()); - - - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("x", 99); - - si = sorig.clone(/* flags=*/ 0); - si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") - .setExtras(pb2).build()); - assertEquals(99, si.getExtras().getInt("x")); - } -} diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java index 5d2924283ef7..0e2a80c8b302 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java @@ -57,7 +57,9 @@ import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; +import android.os.Parcel; import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -76,6 +78,7 @@ import com.android.server.pm.LauncherAppsService.LauncherAppsImpl; import com.android.server.pm.ShortcutService.ConfigConstants; import com.android.server.pm.ShortcutService.FileOutputStreamWithPath; import com.android.server.pm.ShortcutUser.PackageWithUser; +import com.android.server.testutis.TestUtils; import libcore.io.IoUtils; @@ -96,6 +99,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; /** * Tests for ShortcutService and ShortcutManager. @@ -107,10 +111,8 @@ import java.util.Set; -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner * TODO: Add checks with assertAllNotHaveIcon() - * - * TODO: separate, detailed tests for ShortcutInfo (CTS?) * - * - * TODO: Cross-user test (do in CTS?) + * TODO: Detailed test for hasShortcutPermissionInner(). + * TODO: Add tests for the command line functions too. */ @SmallTest public class ShortcutManagerTest extends InstrumentationTestCase { @@ -122,6 +124,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { */ private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true + private static final boolean DUMP_ON_TEARDOWN = false; // DO NOT SUBMIT WITH true + // public for mockito public class BaseContext extends MockContext { @Override @@ -261,7 +265,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { @Override boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { // Sort of hack; do a simpler check. - return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage); + return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage) + || LAUNCHER_3.equals(callingPackage) || LAUNCHER_4.equals(callingPackage); } @Override @@ -394,7 +399,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { private Map<String, PackageInfo> mInjectedPackages; - private ArrayList<PackageWithUser> mUninstalledPackages; + private Set<PackageWithUser> mUninstalledPackages; private PackageManager mMockPackageManager; private PackageManagerInternal mMockPackageManagerInternal; @@ -409,12 +414,21 @@ public class ShortcutManagerTest extends InstrumentationTestCase { private static final String CALLING_PACKAGE_3 = "com.android.test.3"; private static final int CALLING_UID_3 = 10003; + private static final String CALLING_PACKAGE_4 = "com.android.test.4"; + private static final int CALLING_UID_4 = 10004; + private static final String LAUNCHER_1 = "com.android.launcher.1"; private static final int LAUNCHER_UID_1 = 10011; private static final String LAUNCHER_2 = "com.android.launcher.2"; private static final int LAUNCHER_UID_2 = 10012; + private static final String LAUNCHER_3 = "com.android.launcher.3"; + private static final int LAUNCHER_UID_3 = 10013; + + private static final String LAUNCHER_4 = "com.android.launcher.4"; + private static final int LAUNCHER_UID_4 = 10014; + private static final int USER_0 = UserHandle.USER_SYSTEM; private static final int USER_10 = 10; private static final int USER_11 = 11; @@ -438,6 +452,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { private static final int MAX_ICON_DIMENSION_LOWRAM = 32; + private static final ShortcutQuery QUERY_ALL = new ShortcutQuery(); + + static { + QUERY_ALL.setQueryFlags( + ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -457,10 +478,19 @@ public class ShortcutManagerTest extends InstrumentationTestCase { addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1); addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2); addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3); + addPackage(CALLING_PACKAGE_4, CALLING_UID_4, 10); addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4); addPackage(LAUNCHER_2, LAUNCHER_UID_2, 5); + addPackage(LAUNCHER_3, LAUNCHER_UID_3, 6); + addPackage(LAUNCHER_4, LAUNCHER_UID_4, 10); + + // CALLING_PACKAGE_3 / LAUNCHER_3 are not backup target. + updatePackageInfo(CALLING_PACKAGE_3, + pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); + updatePackageInfo(LAUNCHER_3, + pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); - mUninstalledPackages = new ArrayList<>(); + mUninstalledPackages = new HashSet<>(); mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files"); @@ -475,6 +505,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { setCaller(CALLING_PACKAGE_1); } + @Override + protected void tearDown() throws Exception { + if (DUMP_ON_TEARDOWN) dumpsysOnLogcat("Teardown"); + + super.tearDown(); + } + private Context getTestContext() { return getInstrumentation().getContext(); } @@ -504,6 +541,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { return Arrays.asList(array); } + private <T> Set<T> set(Set<T> in) { + return new ArraySet<T>(in); + } + private Signature[] genSignatures(String... signatures) { final Signature[] sigs = new Signature[signatures.length]; for (int i = 0; i < signatures.length; i++){ @@ -529,10 +570,24 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mInjectedPackages.put(packageName, genPackage(packageName, uid, version, signatures)); } + private void updatePackageInfo(String packageName, Consumer<PackageInfo> c) { + c.accept(mInjectedPackages.get(packageName)); + } + private void uninstallPackage(int userId, String packageName) { + if (ENABLE_DUMP) { + Log.i(TAG, "Unnstall package " + packageName + " / " + userId); + } mUninstalledPackages.add(PackageWithUser.of(userId, packageName)); } + private void installPackage(int userId, String packageName) { + if (ENABLE_DUMP) { + Log.i(TAG, "Install package " + packageName + " / " + userId); + } + mUninstalledPackages.remove(PackageWithUser.of(userId, packageName)); + } + PackageInfo getInjectedPackageInfo(String packageName, @UserIdInt int userId, boolean getSignatures) { final PackageInfo pi = mInjectedPackages.get(packageName); @@ -591,14 +646,22 @@ public class ShortcutManagerTest extends InstrumentationTestCase { /** For debugging */ private void dumpsysOnLogcat() { - if (!ENABLE_DUMP) return; + dumpsysOnLogcat(""); + } + + private void dumpsysOnLogcat(String message) { + dumpsysOnLogcat(message, false); + } + + private void dumpsysOnLogcat(String message, boolean force) { + if (force || !ENABLE_DUMP) return; final ByteArrayOutputStream out = new ByteArrayOutputStream(); final PrintWriter pw = new PrintWriter(out); mService.dumpInner(pw); pw.close(); - Log.e(TAG, "Dumping ShortcutService:"); + Log.e(TAG, "Dumping ShortcutService: " + message); for (String line : out.toString().split("\n")) { Log.e(TAG, line); } @@ -608,9 +671,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { * For debugging, dump arbitrary file on logcat. */ private void dumpFileOnLogcat(String path) { + dumpFileOnLogcat(path, ""); + } + + private void dumpFileOnLogcat(String path, String message) { if (!ENABLE_DUMP) return; - Log.i(TAG, "Dumping file: " + path); + Log.i(TAG, "Dumping file: " + path + " " + message); final StringBuilder sb = new StringBuilder(); try (BufferedReader br = new BufferedReader(new FileReader(path))) { String line; @@ -636,10 +703,14 @@ public class ShortcutManagerTest extends InstrumentationTestCase { * For debugging, dump per-user state file on logcat. */ private void dumpUserFile(int userId) { + dumpUserFile(userId, ""); + } + + private void dumpUserFile(int userId, String message) { mService.saveDirtyInfo(); dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath() + "/user-" + userId - + "/" + ShortcutService.FILENAME_USER_PACKAGES); + + "/" + ShortcutService.FILENAME_USER_PACKAGES, message); } private void waitOnMainThread() throws Throwable { @@ -1055,11 +1126,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { return i; } - /** - * Wrap a set in an ArraySet just to get a better toString. - */ - private <T> Set<T> set(Set<T> in) { - return new ArraySet<T>(in); + private ShortcutInfo parceled(ShortcutInfo si) { + Parcel p = Parcel.obtain(); + p.writeParcelable(si, 0); + p.setDataPosition(0); + ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader()); + p.recycle(); + return si2; } /** @@ -1320,8 +1393,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // Still 2 calls left. assertEquals(2, mManager.getRemainingCallCount()); - - // TODO Make sure pinned shortcuts won't be deleted. } public void testDeleteAllDynamicShortcuts() { @@ -1351,8 +1422,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // Still 1 call left assertEquals(1, mManager.getRemainingCallCount()); - - // TODO Make sure pinned shortcuts won't be deleted. } public void testThrottling() { @@ -1877,9 +1946,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { }); } - // TODO: updateShortcuts() - // TODO: getPinnedShortcuts() - // === Test for launcher side APIs === private static ShortcutQuery buildQuery(long changedSince, @@ -1893,6 +1959,20 @@ public class ShortcutManagerTest extends InstrumentationTestCase { return q; } + private static ShortcutQuery buildAllQuery(String packageName) { + final ShortcutQuery q = new ShortcutQuery(); + q.setPackage(packageName); + q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED); + return q; + } + + private static ShortcutQuery buildPinnedQuery(String packageName) { + final ShortcutQuery q = new ShortcutQuery(); + q.setPackage(packageName); + q.setQueryFlags(ShortcutQuery.FLAG_GET_PINNED); + return q; + } + public void testGetShortcuts() { // Set up shortcuts. @@ -3052,6 +3132,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // Remove CALLING_PACKAGE_2 reset(c0); + uninstallPackage(USER_0, CALLING_PACKAGE_2); mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0, USER_0); // Should get a callback with an empty list. @@ -3299,9 +3380,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // Check the registered packages. dumpsysOnLogcat(); assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3326,13 +3407,14 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); // Nonexistent package. + uninstallPackage(USER_0, "abc"); mService.cleanUpPackageLocked("abc", USER_0, USER_0); // No changes. assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3357,12 +3439,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); // Remove a package. + uninstallPackage(USER_0, CALLING_PACKAGE_1); mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0); assertEquals(makeSet(CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3387,12 +3470,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); // Remove a launcher. + uninstallPackage(USER_10, LAUNCHER_1); mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10); assertEquals(makeSet(CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3414,12 +3498,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); // Remove a package. + uninstallPackage(USER_10, CALLING_PACKAGE_2); mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10); assertEquals(makeSet(CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(CALLING_PACKAGE_1), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3441,12 +3526,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); // Remove the other launcher from user 10 too. + uninstallPackage(USER_10, LAUNCHER_2); mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10); assertEquals(makeSet(CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(CALLING_PACKAGE_1), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3468,12 +3554,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); // More remove. + uninstallPackage(USER_10, CALLING_PACKAGE_1); mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10); assertEquals(makeSet(CALLING_PACKAGE_2), - set(user0.getPackages().keySet())); + set(user0.getAllPackages().keySet())); assertEquals(makeSet(), - set(user10.getPackages().keySet())); + set(user10.getAllPackages().keySet())); assertEquals( makeSet(PackageWithUser.of(USER_0, LAUNCHER_1), PackageWithUser.of(USER_0, LAUNCHER_2)), @@ -3494,97 +3581,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.saveDirtyInfo(); } - - public void testSaveAndLoadUser_forBackup() { - // Create some shortcuts. - runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { - assertTrue(mManager.setDynamicShortcuts(list( - makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); - }); - runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { - assertTrue(mManager.setDynamicShortcuts(list( - makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); - }); - runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { - assertTrue(mManager.setDynamicShortcuts(list( - makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); - }); - runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { - assertTrue(mManager.setDynamicShortcuts(list( - makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); - }); - - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0)); - - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0)); - - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0)); - - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10)); - - // Pin some. - - runWithCaller(LAUNCHER_1, USER_0, () -> { - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - list("s1"), HANDLE_USER_0); - - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - list("s2"), UserHandle.of(USER_P0)); - - mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, - list("s3"), HANDLE_USER_0); - }); - - runWithCaller(LAUNCHER_1, USER_P0, () -> { - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - list("s2"), HANDLE_USER_0); - - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - list("s3"), UserHandle.of(USER_P0)); - - mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, - list("s1"), HANDLE_USER_0); - }); - - runWithCaller(LAUNCHER_1, USER_10, () -> { - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - list("s3"), HANDLE_USER_10); - }); - - // Check the state. - - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0)); - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0)); - - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0)); - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0)); - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0)); - - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0)); - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0)); - - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10)); - assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10)); - assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10)); - - // Make sure all the information is persisted. - mService.saveDirtyInfo(); - initService(); - mService.handleUnlockUser(USER_0); - mService.handleUnlockUser(USER_P0); - mService.handleUnlockUser(USER_10); - } - public void testHandleGonePackage_crossProfile() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { @@ -3841,13 +3837,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10)); } - // TODO Detailed test for hasShortcutPermissionInner(). - - // TODO Add tests for the command line functions too. - private void checkCanRestoreTo(boolean expected, ShortcutPackageInfo spi, int version, String... signatures) { - assertEquals(expected, spi.canRestoreTo(genPackage( + assertEquals(expected, spi.canRestoreTo(mService, genPackage( "dummy", /* uid */ 0, version, signatures))); } @@ -3919,6 +3911,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); + uninstallPackage(USER_0, CALLING_PACKAGE_1); mService.mPackageMonitor.onReceive(getTestContext(), genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0)); @@ -3929,6 +3922,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); + uninstallPackage(USER_10, CALLING_PACKAGE_2); mService.mPackageMonitor.onReceive(getTestContext(), genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10)); @@ -3961,7 +3955,1163 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); } - public void testHandlePackageUpdate() { - // TODO: Make sure unshadow is called. + private void backupAndRestore() { + int prevUid = mInjectedCallingUid; + + mInjectedCallingUid = Process.SYSTEM_UID; // Only system can call it. + + dumpsysOnLogcat("Before backup"); + + final byte[] payload = mService.getBackupPayload(USER_0); + if (ENABLE_DUMP) { + final String xml = new String(payload); + Log.i(TAG, "Backup payload:"); + for (String line : xml.split("\n")) { + Log.i(TAG, line); + } + } + + // Before doing anything else, uninstall all packages. + for (int userId : list(USER_0, USER_P0)) { + for (String pkg : list(CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3, + LAUNCHER_1, LAUNCHER_2, LAUNCHER_3)) { + uninstallPackage(userId, pkg); + } + } + + initService(); + mService.applyRestore(payload, USER_0); + + // handleUnlockUser will perform the gone package check, but it shouldn't remove + // shadow information. + mService.handleUnlockUser(USER_0); + + dumpsysOnLogcat("After restore"); + + mInjectedCallingUid = prevUid; + } + + private void prepareCrossProfileDataSet() { + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), + makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6")))); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), + makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6")))); + }); + runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), + makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6")))); + }); + runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list())); + }); + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), + makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6")))); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("x1"), makeShortcut("x2"), makeShortcut("x3"), + makeShortcut("x4"), makeShortcut("x5"), makeShortcut("x6")))); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s1", "s2"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s1", "s2", "s3"), HANDLE_USER_0); + + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s4"), HANDLE_USER_P0); + }); + runWithCaller(LAUNCHER_2, USER_0, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s2", "s3"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s2", "s3", "s4"), HANDLE_USER_0); + + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s5"), HANDLE_USER_P0); + }); + runWithCaller(LAUNCHER_3, USER_0, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5"), HANDLE_USER_0); + + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s6"), HANDLE_USER_P0); + }); + runWithCaller(LAUNCHER_4, USER_0, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list(), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_4, list(), HANDLE_USER_0); + }); + + // Launcher on a managed profile is referring ot user 0! + runWithCaller(LAUNCHER_1, USER_P0, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s4"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4", "s5"), HANDLE_USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5", "s6"), + HANDLE_USER_0); + + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s4", "s1"), HANDLE_USER_P0); + }); + runWithCaller(LAUNCHER_1, USER_10, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("x4", "x5"), HANDLE_USER_10); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("x4", "x5", "x6"), HANDLE_USER_10); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("x4", "x5", "x6", "x1"), + HANDLE_USER_10); + }); + } + + private void prepareForBackupTest() { + + prepareCrossProfileDataSet(); + + backupAndRestore(); + } + + private void assertExistsAndShadow(ShortcutPackageItem spi) { + assertNotNull(spi); + assertTrue(spi.getPackageInfo().isShadow()); + } + + /** + * Make sure the backup data doesn't have the following information: + * - Launchers on other users. + * - Non-backup app information. + * + * But restores all other infomation. + * + * It also omits the following pieces of information, but that's tested in + * {@link #testShortcutInfoSaveAndLoad_forBackup}. + * - Unpinned dynamic shortcuts + * - Bitmaps + */ + public void testBackupAndRestore() { + prepareForBackupTest(); + + checkBackupAndRestore_success(); + } + + public void testBackupAndRestore_backupRestoreTwice() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + dumpsysOnLogcat("Before second backup"); + + backupAndRestore(); + + dumpsysOnLogcat("After second backup"); + + checkBackupAndRestore_success(); + } + + public void testBackupAndRestore_backupRestoreMultiple() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + // This also shouldn't affect the result. + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), + makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6")))); + }); + + backupAndRestore(); + + checkBackupAndRestore_success(); + } + + public void testBackupAndRestore_restoreToNewVersion() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2); + addPackage(LAUNCHER_1, LAUNCHER_UID_1, 5); + + checkBackupAndRestore_success(); + } + + public void testBackupAndRestore_restoreToSuperSetSignatures() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1, "sigx", CALLING_PACKAGE_1); + addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4, LAUNCHER_1, "sigy"); + + checkBackupAndRestore_success(); + } + + private void checkBackupAndRestore_success() { + // Make sure non-system user is not restored. + final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0); + assertEquals(0, userP0.getAllPackages().size()); + assertEquals(0, userP0.getAllLaunchers().size()); + + // Make sure only "allowBackup" apps are restored, and are shadow. + final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0); + assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_1)); + assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_2)); + assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_1))); + assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_2))); + + assertNull(user0.getAllPackages().get(CALLING_PACKAGE_3)); + assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_3))); + assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_P0, LAUNCHER_1))); + + installPackage(USER_0, CALLING_PACKAGE_1); + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2"); + }); + + installPackage(USER_0, LAUNCHER_1); + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)), + "s1"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)) + /* empty, not restored */ ); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty, not restored */ ); + + assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size()); + }); + + installPackage(USER_0, CALLING_PACKAGE_2); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2", "s3"); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)), + "s1"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s1", "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty, not restored */ ); + + assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size()); + }); + + // 3 shouldn't be backed up, so no pinned shortcuts. + installPackage(USER_0, CALLING_PACKAGE_3); + runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + // Launcher on a different profile shouldn't be restored. + runWithCaller(LAUNCHER_1, USER_P0, () -> { + assertEquals(0, + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0) + .size()); + assertEquals(0, + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0) + .size()); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* wasn't restored, so still empty */ ); + }); + + // Package on a different profile, no restore. + installPackage(USER_P0, CALLING_PACKAGE_1); + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + // Restore launcher 2 on user 0. + installPackage(USER_0, LAUNCHER_2); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)), + "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* wasn't restored, so still empty */ ); + + assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size()); + }); + + + // Restoration of launcher2 shouldn't affect other packages; so do the same checks and + // make sure they still have the same result. + installPackage(USER_0, CALLING_PACKAGE_1); + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2"); + }); + + installPackage(USER_0, LAUNCHER_1); + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)), + "s1"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s1", "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* wasn't restored, so still empty */ ); + + assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size()); + }); + + installPackage(USER_0, CALLING_PACKAGE_2); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2", "s3"); + }); + } + + public void testBackupAndRestore_publisherLowerVersion() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version + + checkBackupAndRestore_publisherNotRestored(); + } + + public void testBackupAndRestore_publisherWrongSignature() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature + + checkBackupAndRestore_publisherNotRestored(); + } + + public void testBackupAndRestore_publisherNoLongerBackupTarget() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + updatePackageInfo(CALLING_PACKAGE_1, + pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); + + checkBackupAndRestore_publisherNotRestored(); + } + + private void checkBackupAndRestore_publisherNotRestored() { + installPackage(USER_0, CALLING_PACKAGE_1); + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + installPackage(USER_0, CALLING_PACKAGE_2); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2", "s3"); + }); + + installPackage(USER_0, LAUNCHER_1); + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s1", "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + installPackage(USER_0, LAUNCHER_2); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + + installPackage(USER_0, CALLING_PACKAGE_3); + runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s1", "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + } + + public void testBackupAndRestore_launcherLowerVersion() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version + + checkBackupAndRestore_launcherNotRestored(); + } + + public void testBackupAndRestore_launcherWrongSignature() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature + + checkBackupAndRestore_launcherNotRestored(); + } + + public void testBackupAndRestore_launcherNoLongerBackupTarget() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + updatePackageInfo(LAUNCHER_1, + pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); + + checkBackupAndRestore_launcherNotRestored(); + } + + private void checkBackupAndRestore_launcherNotRestored() { + installPackage(USER_0, CALLING_PACKAGE_1); + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + + // s1 was pinned by launcher 1, which is not restored, yet, so we still see "s1" here. + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2"); + }); + + installPackage(USER_0, CALLING_PACKAGE_2); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2", "s3"); + }); + + // Now we try to restore launcher 1. Then we realize it's not restorable, so L1 has no pinned + // shortcuts. + installPackage(USER_0, LAUNCHER_1); + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + + // Now CALLING_PACKAGE_1 realizes "s1" is no longer pinned. + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s2"); + }); + + installPackage(USER_0, LAUNCHER_2); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)), + "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + + installPackage(USER_0, CALLING_PACKAGE_3); + runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)), + "s2"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + } + + public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() { + prepareForBackupTest(); + + // Note doing a backup & restore again here shouldn't affect the result. + backupAndRestore(); + + updatePackageInfo(CALLING_PACKAGE_1, + pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); + + updatePackageInfo(LAUNCHER_1, + pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); + + checkBackupAndRestore_publisherAndLauncherNotRestored(); + } + + private void checkBackupAndRestore_publisherAndLauncherNotRestored() { + installPackage(USER_0, CALLING_PACKAGE_1); + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + installPackage(USER_0, CALLING_PACKAGE_2); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s1", "s2", "s3"); + }); + + installPackage(USER_0, LAUNCHER_1); + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + installPackage(USER_0, LAUNCHER_2); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + + // Because launcher 1 wasn't restored, "s1" is no longer pinned. + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertShortcutIds(assertAllPinned( + mManager.getPinnedShortcuts()), + "s2", "s3"); + }); + + installPackage(USER_0, CALLING_PACKAGE_3); + runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { + assertEquals(0, mManager.getDynamicShortcuts().size()); + assertEquals(0, mManager.getPinnedShortcuts().size()); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)) + /* empty */); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)), + "s2", "s3"); + assertShortcutIds(assertAllPinned( + mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0)) + /* empty */); + }); + } + + public void testSaveAndLoad_crossProfile() { + prepareCrossProfileDataSet(); + + dumpsysOnLogcat("Before save & load"); + + mService.saveDirtyInfo(); + initService(); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6"); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), + "s1", "s2", "s3", "s4"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6"); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), + "s1", "s2", "s3", "s4", "s5"); + }); + runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6"); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6"); + }); + runWithCaller(CALLING_PACKAGE_4, USER_0, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()) + /* empty */); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()) + /* empty */); + }); + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6"); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_P0, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()) + /* empty */); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()) + /* empty */); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), + "x1", "x2", "x3", "x4", "x5", "x6"); + assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), + "x4", "x5"); + }); + runWithCaller(LAUNCHER_1, USER_0, () -> { + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0), + "s1"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0), + "s1", "s2"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0), + "s1", "s2", "s3"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0), + "s1", "s4"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0) + /* empty */); + TestUtils.assertExpectException( + SecurityException.class, "", () -> { + mLauncherApps.getShortcuts( + buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10); + }); + }); + runWithCaller(LAUNCHER_2, USER_0, () -> { + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0), + "s2"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0), + "s2", "s3"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0), + "s2", "s3", "s4"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0), + "s2", "s5"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0) + /* empty */); + }); + runWithCaller(LAUNCHER_3, USER_0, () -> { + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0), + "s3"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0), + "s3", "s4"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0), + "s3", "s4", "s5"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0), + "s3", "s6"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0) + /* empty */); + }); + runWithCaller(LAUNCHER_4, USER_0, () -> { + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0) + /* empty */); + }); + runWithCaller(LAUNCHER_1, USER_P0, () -> { + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0), + "s3", "s4"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0), + "s3", "s4", "s5"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0), + "s3", "s4", "s5", "s6"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0), + "s1", "s4"); + TestUtils.assertExpectException( + SecurityException.class, "you need to be SYSTEM", () -> { + mLauncherApps.getShortcuts( + buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10); + }); + }); + runWithCaller(LAUNCHER_1, USER_10, () -> { + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_10), + "x4", "x5"); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_10) + /* empty */); + assertShortcutIds( + mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_10) + /* empty */); + TestUtils.assertExpectException( + SecurityException.class, "you need to be SYSTEM", () -> { + mLauncherApps.getShortcuts( + buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0); + }); + TestUtils.assertExpectException( + SecurityException.class, "you need to be SYSTEM", () -> { + mLauncherApps.getShortcuts( + buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0); + }); + }); + } + + // ShortcutInfo tests + + public void testShortcutInfoMissingMandatoryFields() { + TestUtils.assertExpectException( + IllegalArgumentException.class, + "ID must be provided", + () -> new ShortcutInfo.Builder(getTestContext()).build()); + TestUtils.assertExpectException( + IllegalArgumentException.class, + "title must be provided", + () -> new ShortcutInfo.Builder(getTestContext()).setId("id").build() + .enforceMandatoryFields()); + TestUtils.assertExpectException( + NullPointerException.class, + "Intent must be provided", + () -> new ShortcutInfo.Builder(getTestContext()).setId("id").setTitle("x").build() + .enforceMandatoryFields()); + } + + public void testShortcutInfoParcel() { + ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext()) + .setId("id") + .setTitle("title") + .setIntent(makeIntent("action", ShortcutActivity.class)) + .build()); + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals("title", si.getTitle()); + assertEquals("action", si.getIntent().getAction()); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + + si = new ShortcutInfo.Builder(getTestContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithContentUri("content://a.b.c/")) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + si.addFlags(ShortcutInfo.FLAG_PINNED); + si.setBitmapPath("abc"); + si.setIconResourceId(456); + + si = parceled(si); + + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals("content://a.b.c/", si.getIcon().getUriString()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals("abc", si.getBitmapPath()); + assertEquals(456, si.getIconResourceId()); + } + + public void testShortcutInfoClone() { + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithContentUri("content://a.b.c/")) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + sorig.addFlags(ShortcutInfo.FLAG_PINNED); + sorig.setBitmapPath("abc"); + sorig.setIconResourceId(456); + + ShortcutInfo si = sorig.clone(/* clone flags*/ 0); + + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals("content://a.b.c/", si.getIcon().getUriString()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals("abc", si.getBitmapPath()); + assertEquals(456, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); + + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + assertEquals(0, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals(null, si.getIntent()); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + assertEquals(0, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); + + assertEquals(getTestContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(null, si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals(null, si.getTitle()); + assertEquals(null, si.getText()); + assertEquals(null, si.getIntent()); + assertEquals(0, si.getWeight()); + assertEquals(null, si.getExtras()); + + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + assertEquals(0, si.getIconResourceId()); + } + + public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException { + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithContentUri("content://a.b.c/")) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + sorig.addFlags(ShortcutInfo.FLAG_PINNED); + sorig.setBitmapPath("abc"); + sorig.setIconResourceId(456); + + ShortcutInfo si; + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setActivityComponent(new ComponentName("x", "y")).build()); + assertEquals(new ComponentName("x", "y"), si.getActivityComponent()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setIcon(Icon.createWithContentUri("content://x.y.z/")).build()); + assertEquals("content://x.y.z/", si.getIcon().getUriString()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTitle("xyz").build()); + assertEquals("xyz", si.getTitle()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setText("xxx").build()); + assertEquals("xxx", si.getText()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setIntent(makeIntent("action2", ShortcutActivity.class)).build()); + assertEquals("action2", si.getIntent().getAction()); + assertEquals(null, si.getIntent().getStringExtra("key")); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build()); + assertEquals("action3", si.getIntent().getAction()); + assertEquals("x", si.getIntent().getStringExtra("key")); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setWeight(999).build()); + assertEquals(999, si.getWeight()); + + + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("x", 99); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setExtras(pb2).build()); + assertEquals(99, si.getExtras().getInt("x")); + + final long timestamp = si.getLastChangedTimestamp(); + Thread.sleep(2); + + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") + .setTitle("xyz").build()); + + assertTrue(si.getLastChangedTimestamp() > timestamp); + } + + public void testShortcutInfoSaveAndLoad() throws InterruptedException { + setCaller(CALLING_PACKAGE_1, USER_0); + + final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( + getTestContext().getResources(), R.drawable.black_32x32)); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIcon(bmp32x32) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + + mManager.addDynamicShortcut(sorig); + + Thread.sleep(2); + final long now = System.currentTimeMillis(); + + // Save and load. + mService.saveDirtyInfo(); + initService(); + mService.handleUnlockUser(USER_0); + + ShortcutInfo si; + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0); + + assertEquals(CALLING_PACKAGE_1, si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName()); + assertEquals(null, si.getIcon()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags()); + assertNotNull(si.getBitmapPath()); // Something should be set. + assertEquals(0, si.getIconResourceId()); + assertTrue(si.getLastChangedTimestamp() < now); + } + + public void testShortcutInfoSaveAndLoad_forBackup() { + setCaller(CALLING_PACKAGE_1, USER_0); + + final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( + getTestContext().getResources(), R.drawable.black_32x32)); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) + .setId("id") + .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class)) + .setIcon(bmp32x32) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + + mManager.addDynamicShortcut(sorig); + + // Dynamic shortcuts won't be backed up, so we need to pin it. + setCaller(LAUNCHER_1, USER_0); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id"), HANDLE_USER_0); + + // Do backup & restore. + backupAndRestore(); + + ShortcutInfo si; + si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0); + + assertEquals(CALLING_PACKAGE_1, si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName()); + assertEquals(null, si.getIcon()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertNull(si.getBitmapPath()); // No icon. + assertEquals(0, si.getIconResourceId()); + } + + public void testDumpsys_crossProfile() { + prepareCrossProfileDataSet(); + dumpsysOnLogcat("test1", /* force= */ true); + } + + public void testDumpsys_withIcons() { + testIcons(); + // Dump after having some icons. + dumpsysOnLogcat("test1", /* force= */ true); } } diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index b56ce736aef6..dbc2b0c9f850 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -170,6 +170,15 @@ public final class PhoneAccount implements Parcelable { public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 0x100; /** + * Flag indicating that for this {@link PhoneAccount}, emergency video calling is allowed. + * <p> + * When set, Telecom will allow emergency video calls to be placed. When not set, Telecom will + * convert all outgoing video calls to emergency numbers to audio-only. + * @hide + */ + public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200; + + /** * URI scheme for telephone number URIs. */ public static final String SCHEME_TEL = "tel"; @@ -731,6 +740,9 @@ public final class PhoneAccount implements Parcelable { if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) { sb.append("PlaceEmerg "); } + if (hasCapabilities(CAPABILITY_EMERGENCY_VIDEO_CALLING)) { + sb.append("EmergVideo "); + } if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) { sb.append("SimSub "); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c69a360aeb5e..461611d0114c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -252,6 +252,16 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool"; /** + * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi + * calling settings will not include an option for "wifi only". If true, the wifi calling + * settings will include an option for "wifi only" + * <p> + * By default, it is assumed that WFC supports "wifi only". + */ + public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = + "carrier_wfc_supports_wifi_only_bool"; + + /** * Default WFC_IMS_mode 0: WIFI_ONLY * 1: CELLULAR_PREFERRED * 2: WIFI_PREFERRED @@ -628,6 +638,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false); + sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false); sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2); diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index ed7351f85a7a..033312b76edd 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -62,7 +62,7 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts"; private static final String WEARABLE_ACTION_GOOGLE = "com.google.android.wearable.action.GOOGLE"; - private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle + private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java index ad02d2b77a99..c0583ceb2fab 100644 --- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java +++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java @@ -18,6 +18,7 @@ package android.hardware.soundtrigger; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -40,6 +41,7 @@ import java.io.DataOutputStream; import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; +import java.util.HashSet; import java.util.Random; import java.util.UUID; @@ -53,7 +55,7 @@ public class GenericSoundModelTest extends AndroidTestCase { static final int MSG_GENERIC_TRIGGER = 4; private Random random = new Random(); - private ArrayList<UUID> loadedModelUuids; + private HashSet<UUID> loadedModelUuids; private ISoundTriggerService soundTriggerService; private SoundTriggerManager soundTriggerManager; @@ -68,7 +70,7 @@ public class GenericSoundModelTest extends AndroidTestCase { soundTriggerManager = (SoundTriggerManager) context.getSystemService( Context.SOUND_TRIGGER_SERVICE); - loadedModelUuids = new ArrayList<UUID>(); + loadedModelUuids = new HashSet<UUID>(); } @Override @@ -170,6 +172,101 @@ public class GenericSoundModelTest extends AndroidTestCase { verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any()); } + /** + * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping + * recognition. Intended to find unexpected errors that occur in unexpected states. + */ + @LargeTest + public void testFuzzGenericSoundModel() throws Exception { + int numModels = 2; + + final int STATUS_UNLOADED = 0; + final int STATUS_LOADED = 1; + final int STATUS_STARTED = 2; + + class ModelInfo { + int status; + GenericSoundModel model; + + public ModelInfo(GenericSoundModel model, int status) { + this.status = status; + this.model = model; + } + } + + Random predictableRandom = new Random(100); + + ArrayList modelInfos = new ArrayList<ModelInfo>(); + for(int i=0; i<numModels; i++) { + // Create sound model + byte[] data = new byte[1024]; + predictableRandom.nextBytes(data); + UUID modelUuid = UUID.randomUUID(); + UUID mVendorUuid = UUID.randomUUID(); + GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data); + ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED); + modelInfos.add(modelInfo); + } + + boolean captureTriggerAudio = true; + boolean allowMultipleTriggers = true; + RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers, + null, null); + TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback()); + + + int numOperationsToRun = 100; + for(int i=0; i<numOperationsToRun; i++) { + // Select a random model + int modelInfoIndex = predictableRandom.nextInt(modelInfos.size()); + ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex); + + // Perform a random operation + int operation = predictableRandom.nextInt(5); + + if (operation == 0 && modelInfo.status == STATUS_UNLOADED) { + // Update and start sound model + soundTriggerService.updateSoundModel(modelInfo.model); + loadedModelUuids.add(modelInfo.model.uuid); + modelInfo.status = STATUS_LOADED; + } else if (operation == 1 && modelInfo.status == STATUS_LOADED) { + // Start the sound model + int r = soundTriggerService.startRecognition(new ParcelUuid(modelInfo.model.uuid), + spyCallback, config); + assertEquals("Could Not Start Recognition with code: " + r, + android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r); + modelInfo.status = STATUS_STARTED; + } else if (operation == 2 && modelInfo.status == STATUS_STARTED) { + // Send trigger to stub HAL + Socket socket = new Socket(InetAddress.getLocalHost(), 14035); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + out.writeBytes("trig " + modelInfo.model.uuid + "\r\n"); + out.flush(); + socket.close(); + + // Verify trigger was received + verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any()); + reset(spyCallback); + } else if (operation == 3 && modelInfo.status == STATUS_STARTED) { + // Stop recognition + int r = soundTriggerService.stopRecognition(new ParcelUuid(modelInfo.model.uuid), + spyCallback); + assertEquals("Could Not Stop Recognition with code: " + r, + android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r); + modelInfo.status = STATUS_LOADED; + } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) { + // Delete sound model + soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.uuid)); + loadedModelUuids.remove(modelInfo.model.uuid); + + // Confirm it was deleted + GenericSoundModel returnedModel = + soundTriggerService.getSoundModel(new ParcelUuid(modelInfo.model.uuid)); + assertEquals(null, returnedModel); + modelInfo.status = STATUS_UNLOADED; + } + } + } public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub { @Override diff --git a/tests/VectorDrawableTest/Android.mk b/tests/VectorDrawableTest/Android.mk index 3d44e337bc56..dd8a4d474ae2 100644 --- a/tests/VectorDrawableTest/Android.mk +++ b/tests/VectorDrawableTest/Android.mk @@ -23,6 +23,4 @@ LOCAL_PACKAGE_NAME := VectorDrawableTest LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current - include $(BUILD_PACKAGE) diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml index e648897b6274..7b3beb27e0cc 100644 --- a/tests/VectorDrawableTest/AndroidManifest.xml +++ b/tests/VectorDrawableTest/AndroidManifest.xml @@ -18,8 +18,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.dynamic" > - <uses-sdk android:minSdkVersion="21" /> - <application android:hardwareAccelerated="true" android:label="vector" diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java index 4026e5ec6bdd..8f538aee78aa 100644 --- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java @@ -24,6 +24,7 @@ import android.view.View; import android.widget.Button; import android.widget.GridLayout; import android.widget.ScrollView; +import android.widget.TextView; public class AnimatedVectorDrawableTest extends Activity implements View.OnClickListener { private static final String LOGCAT = "AnimatedVectorDrawableTest"; @@ -45,35 +46,51 @@ public class AnimatedVectorDrawableTest extends Activity implements View.OnClick @Override protected void onCreate(Bundle savedInstanceState) { final int[] layerTypes = {View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE}; + final boolean[] forceOnUi = {false, true}; super.onCreate(savedInstanceState); ScrollView scrollView = new ScrollView(this); GridLayout container = new GridLayout(this); scrollView.addView(container); - container.setColumnCount(2); + container.setColumnCount(layerTypes.length * forceOnUi.length); + for (int j = 0; j < layerTypes.length; j++) { + for (int k = 0; k < forceOnUi.length; k++) { + TextView textView = new TextView(this); + String category = "Layer:" + + (layerTypes[j] == View.LAYER_TYPE_SOFTWARE ? "SW" : "HW") + + (forceOnUi[k] == true ? ",forceUI" : ""); + textView.setText(category); + container.addView(textView); + } + } for (int i = 0; i < icon.length; i++) { for (int j = 0; j < layerTypes.length; j++) { - Button button = new Button(this); - button.setWidth(400); - button.setHeight(400); - button.setLayerType(layerTypes[j], null); - button.setBackgroundResource(icon[i]); - AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground(); - d.registerAnimationCallback(new Animatable2.AnimationCallback() { - @Override - public void onAnimationStart(Drawable drawable) { - Log.v(LOGCAT, "Animator start"); + for (int k = 0; k < forceOnUi.length; k++) { + Button button = new Button(this); + button.setWidth(300); + button.setHeight(300); + button.setLayerType(layerTypes[j], null); + button.setBackgroundResource(icon[i]); + AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground(); + if (forceOnUi[k] == true) { + d.forceAnimationOnUI(); } + d.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationStart(Drawable drawable) { + Log.v(LOGCAT, "Animator start"); + } - @Override - public void onAnimationEnd(Drawable drawable) { - Log.v(LOGCAT, "Animator end"); - } - }); + @Override + public void onAnimationEnd(Drawable drawable) { + Log.v(LOGCAT, "Animator end"); + } + }); - container.addView(button); - button.setOnClickListener(this); + container.addView(button); + button.setOnClickListener(this); + } } } diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index 85d22ffacd4c..876a422a2678 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -54,6 +54,7 @@ sources := \ Debug.cpp \ Flags.cpp \ java/AnnotationProcessor.cpp \ + java/ClassDefinition.cpp \ java/JavaClassGenerator.cpp \ java/ManifestClassGenerator.cpp \ java/ProguardRules.cpp \ diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp index 496e92e0bf93..ba744395ece0 100644 --- a/tools/aapt2/java/AnnotationProcessor.cpp +++ b/tools/aapt2/java/AnnotationProcessor.cpp @@ -64,7 +64,7 @@ void AnnotationProcessor::appendNewLine() { mComment << "\n *"; } -void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) { +void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) const { if (mHasComments) { std::string result = mComment.str(); for (StringPiece line : util::tokenize<char>(result, '\n')) { diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h index fadf58440991..0fc5b08e0bf3 100644 --- a/tools/aapt2/java/AnnotationProcessor.h +++ b/tools/aapt2/java/AnnotationProcessor.h @@ -66,7 +66,7 @@ public: /** * Writes the comments and annotations to the stream, with the given prefix before each line. */ - void writeToStream(std::ostream* out, const StringPiece& prefix); + void writeToStream(std::ostream* out, const StringPiece& prefix) const; private: enum : uint32_t { diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp new file mode 100644 index 000000000000..08f2c8b9805c --- /dev/null +++ b/tools/aapt2/java/ClassDefinition.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "java/ClassDefinition.h" +#include "util/StringPiece.h" + +#include <ostream> + +namespace aapt { + +bool ClassDefinition::empty() const { + for (const std::unique_ptr<ClassMember>& member : mMembers) { + if (!member->empty()) { + return false; + } + } + return true; +} + +void ClassDefinition::writeToStream(const StringPiece& prefix, bool final, + std::ostream* out) const { + if (mMembers.empty() && !mCreateIfEmpty) { + return; + } + + ClassMember::writeToStream(prefix, final, out); + + *out << prefix << "public "; + if (mQualifier == ClassQualifier::Static) { + *out << "static "; + } + *out << "final class " << mName << " {\n"; + + std::string newPrefix = prefix.toString(); + newPrefix.append(kIndent); + + for (const std::unique_ptr<ClassMember>& member : mMembers) { + member->writeToStream(newPrefix, final, out); + *out << "\n"; + } + + *out << prefix << "}"; +} + +constexpr static const char* sWarningHeader = + "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" + " *\n" + " * This class was automatically generated by the\n" + " * aapt tool from the resource data it found. It\n" + " * should not be modified by hand.\n" + " */\n\n"; + +bool ClassDefinition::writeJavaFile(const ClassDefinition* def, + const StringPiece& package, + bool final, + std::ostream* out) { + *out << sWarningHeader << "package " << package << ";\n\n"; + def->writeToStream("", final, out); + return bool(*out); +} + +} // namespace aapt diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h new file mode 100644 index 000000000000..53e0f6fe2f1e --- /dev/null +++ b/tools/aapt2/java/ClassDefinition.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef AAPT_JAVA_CLASSDEFINITION_H +#define AAPT_JAVA_CLASSDEFINITION_H + +#include "Resource.h" +#include "java/AnnotationProcessor.h" +#include "util/StringPiece.h" +#include "util/Util.h" + +#include <android-base/macros.h> +#include <sstream> +#include <string> + +namespace aapt { + +// The number of attributes to emit per line in a Styleable array. +constexpr static size_t kAttribsPerLine = 4; +constexpr static const char* kIndent = " "; + +class ClassMember { +public: + virtual ~ClassMember() = default; + + AnnotationProcessor* getCommentBuilder() { + return &mProcessor; + } + + virtual bool empty() const = 0; + + virtual void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const { + mProcessor.writeToStream(out, prefix); + } + +private: + AnnotationProcessor mProcessor; +}; + +template <typename T> +class PrimitiveMember : public ClassMember { +public: + PrimitiveMember(const StringPiece& name, const T& val) : + mName(name.toString()), mVal(val) { + } + + bool empty() const override { + return false; + } + + void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override { + ClassMember::writeToStream(prefix, final, out); + + *out << prefix << "public static " << (final ? "final " : "") + << "int " << mName << "=" << mVal << ";"; + } + +private: + std::string mName; + T mVal; + + DISALLOW_COPY_AND_ASSIGN(PrimitiveMember); +}; + +/** + * Specialization for strings so they get the right type and are quoted with "". + */ +template <> +class PrimitiveMember<std::string> : public ClassMember { +public: + PrimitiveMember(const StringPiece& name, const std::string& val) : + mName(name.toString()), mVal(val) { + } + + bool empty() const override { + return false; + } + + void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override { + ClassMember::writeToStream(prefix, final, out); + + *out << prefix << "public static " << (final ? "final " : "") + << "String " << mName << "=\"" << mVal << "\";"; + } + +private: + std::string mName; + std::string mVal; + + DISALLOW_COPY_AND_ASSIGN(PrimitiveMember); +}; + +using IntMember = PrimitiveMember<uint32_t>; +using ResourceMember = PrimitiveMember<ResourceId>; +using StringMember = PrimitiveMember<std::string>; + +template <typename T> +class PrimitiveArrayMember : public ClassMember { +public: + PrimitiveArrayMember(const StringPiece& name) : + mName(name.toString()) { + } + + void addElement(const T& val) { + mElements.push_back(val); + } + + bool empty() const override { + return false; + } + + void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override { + ClassMember::writeToStream(prefix, final, out); + + *out << "public static final int[] " << mName << "={"; + + const auto begin = mElements.begin(); + const auto end = mElements.end(); + for (auto current = begin; current != end; ++current) { + if (std::distance(begin, current) % kAttribsPerLine == 0) { + *out << "\n" << prefix << kIndent << kIndent; + } + + *out << *current; + if (std::distance(current, end) > 1) { + *out << ", "; + } + } + *out << "\n" << prefix << kIndent <<"};"; + } + +private: + std::string mName; + std::vector<T> mElements; + + DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember); +}; + +using ResourceArrayMember = PrimitiveArrayMember<ResourceId>; + +enum class ClassQualifier { + None, + Static +}; + +class ClassDefinition : public ClassMember { +public: + static bool writeJavaFile(const ClassDefinition* def, + const StringPiece& package, + bool final, + std::ostream* out); + + ClassDefinition(const StringPiece& name, ClassQualifier qualifier, bool createIfEmpty) : + mName(name.toString()), mQualifier(qualifier), mCreateIfEmpty(createIfEmpty) { + } + + void addMember(std::unique_ptr<ClassMember> member) { + mMembers.push_back(std::move(member)); + } + + bool empty() const override; + void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override; + +private: + std::string mName; + ClassQualifier mQualifier; + bool mCreateIfEmpty; + std::vector<std::unique_ptr<ClassMember>> mMembers; + + DISALLOW_COPY_AND_ASSIGN(ClassDefinition); +}; + +} // namespace aapt + +#endif /* AAPT_JAVA_CLASSDEFINITION_H */ diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h deleted file mode 100644 index cf92c9aa6f89..000000000000 --- a/tools/aapt2/java/ClassDefinitionWriter.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#ifndef AAPT_JAVA_CLASSDEFINITION_H -#define AAPT_JAVA_CLASSDEFINITION_H - -#include "Resource.h" -#include "java/AnnotationProcessor.h" -#include "util/StringPiece.h" -#include "util/Util.h" - -#include <sstream> -#include <string> - -namespace aapt { - -struct ClassDefinitionWriterOptions { - bool useFinalQualifier = false; - bool forceCreationIfEmpty = false; -}; - -/** - * Writes a class for use in R.java or Manifest.java. - */ -class ClassDefinitionWriter { -public: - ClassDefinitionWriter(const StringPiece& name, const ClassDefinitionWriterOptions& options) : - mName(name.toString()), mOptions(options), mStarted(false) { - } - - ClassDefinitionWriter(const StringPiece16& name, const ClassDefinitionWriterOptions& options) : - mName(util::utf16ToUtf8(name)), mOptions(options), mStarted(false) { - } - - void addIntMember(const StringPiece& name, AnnotationProcessor* processor, - const uint32_t val) { - ensureClassDeclaration(); - if (processor) { - processor->writeToStream(&mOut, kIndent); - } - mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "") - << "int " << name << "=" << val << ";\n"; - } - - void addStringMember(const StringPiece16& name, AnnotationProcessor* processor, - const StringPiece16& val) { - ensureClassDeclaration(); - if (processor) { - processor->writeToStream(&mOut, kIndent); - } - mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "") - << "String " << name << "=\"" << val << "\";\n"; - } - - void addResourceMember(const StringPiece& name, AnnotationProcessor* processor, - const ResourceId id) { - ensureClassDeclaration(); - if (processor) { - processor->writeToStream(&mOut, kIndent); - } - mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "") - << "int " << name << "=" << id <<";\n"; - } - - template <typename Iterator, typename FieldAccessorFunc> - void addArrayMember(const StringPiece& name, AnnotationProcessor* processor, - const Iterator begin, const Iterator end, FieldAccessorFunc f) { - ensureClassDeclaration(); - if (processor) { - processor->writeToStream(&mOut, kIndent); - } - mOut << kIndent << "public static final int[] " << name << "={"; - - for (Iterator current = begin; current != end; ++current) { - if (std::distance(begin, current) % kAttribsPerLine == 0) { - mOut << "\n" << kIndent << kIndent; - } - - mOut << f(*current); - if (std::distance(current, end) > 1) { - mOut << ", "; - } - } - mOut << "\n" << kIndent <<"};\n"; - } - - void writeToStream(std::ostream* out, const StringPiece& prefix, - AnnotationProcessor* processor=nullptr) { - if (mOptions.forceCreationIfEmpty) { - ensureClassDeclaration(); - } - - if (!mStarted) { - return; - } - - if (processor) { - processor->writeToStream(out, prefix); - } - - std::string result = mOut.str(); - for (StringPiece line : util::tokenize<char>(result, '\n')) { - *out << prefix << line << "\n"; - } - *out << prefix << "}\n"; - } - -private: - constexpr static const char* kIndent = " "; - - // The number of attributes to emit per line in a Styleable array. - constexpr static size_t kAttribsPerLine = 4; - - void ensureClassDeclaration() { - if (!mStarted) { - mStarted = true; - mOut << "public static final class " << mName << " {\n"; - } - } - - std::stringstream mOut; - std::string mName; - ClassDefinitionWriterOptions mOptions; - bool mStarted; -}; - -} // namespace aapt - -#endif /* AAPT_JAVA_CLASSDEFINITION_H */ diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index 01330dc6b5a1..2d076c2d5a66 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -21,7 +21,7 @@ #include "ValueVisitor.h" #include "java/AnnotationProcessor.h" -#include "java/ClassDefinitionWriter.h" +#include "java/ClassDefinition.h" #include "java/JavaClassGenerator.h" #include "process/SymbolTable.h" #include "util/StringPiece.h" @@ -39,16 +39,6 @@ JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* tab mContext(context), mTable(table), mOptions(options) { } -static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) { - *out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" - " *\n" - " * This class was automatically generated by the\n" - " * aapt tool from the resource data it found. It\n" - " * should not be modified by hand.\n" - " */\n\n" - "package " << packageNameToGenerate << ";\n\n"; -} - static const std::set<StringPiece16> sJavaIdentifiers = { u"abstract", u"assert", u"boolean", u"break", u"byte", u"case", u"catch", u"char", u"class", u"const", u"continue", @@ -110,15 +100,15 @@ static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* att if (typeMask & android::ResTable_map::TYPE_REFERENCE) { processor->appendComment( "<p>May be a reference to another resource, in the form\n" - "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n" - "attribute in the form\n" - "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\"."); + "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n" + "attribute in the form\n" + "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\"."); } if (typeMask & android::ResTable_map::TYPE_STRING) { processor->appendComment( "<p>May be a string value, using '\\\\;' to escape characters such as\n" - "'\\\\n' or '\\\\uxxxx' for a unicode character;"); + "'\\\\n' or '\\\\uxxxx' for a unicode character;"); } if (typeMask & android::ResTable_map::TYPE_INTEGER) { @@ -128,14 +118,14 @@ static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* att if (typeMask & android::ResTable_map::TYPE_BOOLEAN) { processor->appendComment( "<p>May be a boolean value, such as \"<code>true</code>\" or\n" - "\"<code>false</code>\"."); + "\"<code>false</code>\"."); } if (typeMask & android::ResTable_map::TYPE_COLOR) { processor->appendComment( "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n" - "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n" - "\"<code>#<i>aarrggbb</i></code>\"."); + "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n" + "\"<code>#<i>aarrggbb</i></code>\"."); } if (typeMask & android::ResTable_map::TYPE_FLOAT) { @@ -146,33 +136,33 @@ static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* att if (typeMask & android::ResTable_map::TYPE_DIMENSION) { processor->appendComment( "<p>May be a dimension value, which is a floating point number appended with a\n" - "unit such as \"<code>14.5sp</code>\".\n" - "Available units are: px (pixels), dp (density-independent pixels),\n" - "sp (scaled pixels based on preferred font size), in (inches), and\n" - "mm (millimeters)."); + "unit such as \"<code>14.5sp</code>\".\n" + "Available units are: px (pixels), dp (density-independent pixels),\n" + "sp (scaled pixels based on preferred font size), in (inches), and\n" + "mm (millimeters)."); } if (typeMask & android::ResTable_map::TYPE_FRACTION) { processor->appendComment( "<p>May be a fractional value, which is a floating point number appended with\n" - "either % or %p, such as \"<code>14.5%</code>\".\n" - "The % suffix always means a percentage of the base size;\n" - "the optional %p suffix provides a size relative to some parent container."); + "either % or %p, such as \"<code>14.5%</code>\".\n" + "The % suffix always means a percentage of the base size;\n" + "the optional %p suffix provides a size relative to some parent container."); } if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) { if (typeMask & android::ResTable_map::TYPE_FLAGS) { processor->appendComment( "<p>Must be one or more (separated by '|') of the following " - "constant values.</p>"); + "constant values.</p>"); } else { processor->appendComment("<p>Must be one of the following constant values.</p>"); } processor->appendComment("<table>\n<colgroup align=\"left\" />\n" - "<colgroup align=\"left\" />\n" - "<colgroup align=\"left\" />\n" - "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n"); + "<colgroup align=\"left\" />\n" + "<colgroup align=\"left\" />\n" + "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n"); for (const Attribute::Symbol& symbol : attr->symbols) { std::stringstream line; line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>" @@ -214,13 +204,15 @@ static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs } } -void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef, - AnnotationProcessor* processor, - const StringPiece16& packageNameToGenerate, - const std::u16string& entryName, - const Styleable* styleable) { +void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& packageNameToGenerate, + const std::u16string& entryName, + const Styleable* styleable, + ClassDefinition* outStyleableClassDef) { const std::string className = transform(entryName); + std::unique_ptr<ResourceArrayMember> styleableArrayDef = + util::make_unique<ResourceArrayMember>(className); + // This must be sorted by resource ID. std::vector<StyleableAttr> sortedAttributes; sortedAttributes.reserve(styleable->entries.size()); @@ -230,6 +222,8 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry"); assert(attr.name && "no name set for Styleable entry"); + // We will need the unmangled, transformed name in the comments and the field, + // so create it once and cache it in this StyleableAttr data structure. StyleableAttr styleableAttr = {}; styleableAttr.attrRef = &attr; styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className, @@ -247,6 +241,8 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC mangledReference.name = mangledName; } + // Look up the symbol so that we can write out in the comments what are possible + // legal values for this attribute. const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference( mangledReference); if (symbol) { @@ -254,10 +250,11 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC } sortedAttributes.push_back(std::move(styleableAttr)); } + + // Sort the attributes by ID. std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr); const size_t attrCount = sortedAttributes.size(); - if (attrCount > 0) { // Build the comment string for the Styleable. It includes details about the // child attributes. @@ -267,6 +264,7 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC } else { styleableComment << "Attributes that can be used with a " << className << ".\n"; } + styleableComment << "<p>Includes the following attributes:</p>\n" "<table>\n" @@ -274,7 +272,7 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC "<colgroup align=\"left\" />\n" "<tr><th>Attribute</th><th>Description</th></tr>\n"; - for (const auto& entry : sortedAttributes) { + for (const StyleableAttr& entry : sortedAttributes) { const ResourceName& attrName = entry.attrRef->name.value(); styleableComment << "<tr><td>"; styleableComment << "<code>{@link #" @@ -292,21 +290,22 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC styleableComment << "</td></tr>\n"; } styleableComment << "</table>\n"; - for (const auto& entry : sortedAttributes) { + + for (const StyleableAttr& entry : sortedAttributes) { styleableComment << "@see #" << entry.fieldName << "\n"; } - processor->appendComment(styleableComment.str()); + + styleableArrayDef->getCommentBuilder()->appendComment(styleableComment.str()); } - auto accessorFunc = [](const StyleableAttr& a) -> ResourceId { - return a.attrRef->id ? a.attrRef->id.value() : ResourceId(0); - }; + // Add the ResourceIds to the array member. + for (const StyleableAttr& styleableAttr : sortedAttributes) { + styleableArrayDef->addElement( + styleableAttr.attrRef->id ? styleableAttr.attrRef->id.value() : ResourceId(0)); + } - // First we emit the array containing the IDs of each attribute. - outClassDef->addArrayMember(className, processor, - sortedAttributes.begin(), - sortedAttributes.end(), - accessorFunc); + // Add the Styleable array to the Styleable class. + outStyleableClassDef->addMember(std::move(styleableArrayDef)); // Now we emit the indices into the array. for (size_t i = 0; i < attrCount; i++) { @@ -318,7 +317,10 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC packageName = mContext->getCompilationPackage(); } - AnnotationProcessor attrProcessor; + std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>( + sortedAttributes[i].fieldName, i); + + AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder(); StringPiece16 comment = styleableAttr.attrRef->getComment(); if (styleableAttr.attribute && comment.empty()) { @@ -326,8 +328,8 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC } if (!comment.empty()) { - attrProcessor.appendComment("<p>\n@attr description"); - attrProcessor.appendComment(comment); + attrProcessor->appendComment("<p>\n@attr description"); + attrProcessor->appendComment(comment); } else { std::stringstream defaultComment; defaultComment @@ -335,27 +337,29 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n" << "attribute's value can be found in the " << "{@link #" << className << "} array."; - attrProcessor.appendComment(defaultComment.str()); + attrProcessor->appendComment(defaultComment.str()); } - attrProcessor.appendNewLine(); + attrProcessor->appendNewLine(); if (styleableAttr.attribute) { - addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get()); - attrProcessor.appendNewLine(); + addAttributeFormatDoc(attrProcessor, styleableAttr.attribute.get()); + attrProcessor->appendNewLine(); } std::stringstream doclavaName; doclavaName << "@attr name " << packageName << ":" << attrName.entry;; - attrProcessor.appendComment(doclavaName.str()); - outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i); + attrProcessor->appendComment(doclavaName.str()); + + outStyleableClassDef->addMember(std::move(indexMember)); } } -bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef, - const StringPiece16& packageNameToGenerate, - const ResourceTablePackage* package, - const ResourceTableType* type) { +bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameToGenerate, + const ResourceTablePackage* package, + const ResourceTableType* type, + ClassDefinition* outTypeClassDef) { + for (const auto& entry : type->entries) { if (skipSymbol(entry->symbolStatus.state)) { continue; @@ -389,33 +393,41 @@ bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef return false; } - // Build the comments and annotations for this entry. - - AnnotationProcessor processor; - if (entry->symbolStatus.state != SymbolState::kUndefined) { - processor.appendComment(entry->symbolStatus.comment); - } - - for (const auto& configValue : entry->values) { - processor.appendComment(configValue->value->getComment()); - } - - // If this is an Attribute, append the format Javadoc. - if (!entry->values.empty()) { - if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) { - // We list out the available values for the given attribute. - addAttributeFormatDoc(&processor, attr); - } - } - if (type->type == ResourceType::kStyleable) { assert(!entry->values.empty()); + const Styleable* styleable = static_cast<const Styleable*>( entry->values.front()->value.get()); - writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate, - unmangledName, styleable); + + // Comments are handled within this method. + addMembersToStyleableClass(packageNameToGenerate, unmangledName, styleable, + outTypeClassDef); } else { - outClassDef->addResourceMember(transform(unmangledName), &processor, id); + std::unique_ptr<ResourceMember> resourceMember = + util::make_unique<ResourceMember>(transform(unmangledName), id); + + // Build the comments and annotations for this entry. + AnnotationProcessor* processor = resourceMember->getCommentBuilder(); + + // Add the comments from any <public> tags. + if (entry->symbolStatus.state != SymbolState::kUndefined) { + processor->appendComment(entry->symbolStatus.comment); + } + + // Add the comments from all configurations of this entry. + for (const auto& configValue : entry->values) { + processor->appendComment(configValue->value->getComment()); + } + + // If this is an Attribute, append the format Javadoc. + if (!entry->values.empty()) { + if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) { + // We list out the available values for the given attribute. + addAttributeFormatDoc(processor, attr); + } + } + + outTypeClassDef->addMember(std::move(resourceMember)); } } return true; @@ -427,9 +439,8 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, st bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, const StringPiece16& outPackageName, std::ostream* out) { - generateHeader(outPackageName, out); - *out << "public final class R {\n"; + ClassDefinition rClass("R", ClassQualifier::None, true); for (const auto& package : mTable->packages) { for (const auto& type : package->types) { @@ -437,13 +448,15 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, continue; } - ClassDefinitionWriterOptions classOptions; - classOptions.useFinalQualifier = mOptions.useFinal; - classOptions.forceCreationIfEmpty = + const bool forceCreationIfEmpty = (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic); - ClassDefinitionWriter classDef(toString(type->type), classOptions); - bool result = writeEntriesForClass(&classDef, packageNameToGenerate, - package.get(), type.get()); + + std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>( + util::utf16ToUtf8(toString(type->type)), ClassQualifier::Static, + forceCreationIfEmpty); + + bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(), + classDef.get()); if (!result) { return false; } @@ -452,26 +465,31 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, // Also include private attributes in this same class. ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate); if (privType) { - result = writeEntriesForClass(&classDef, packageNameToGenerate, - package.get(), privType); + result = addMembersToTypeClass(packageNameToGenerate, package.get(), privType, + classDef.get()); if (!result) { return false; } } } - AnnotationProcessor processor; if (type->type == ResourceType::kStyleable && mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) { // When generating a public R class, we don't want Styleable to be part of the API. // It is only emitted for documentation purposes. - processor.appendComment("@doconly"); + AnnotationProcessor* processor = classDef->getCommentBuilder(); + processor->appendComment("@doconly"); } - classDef.writeToStream(out, " ", &processor); + + rClass.addMember(std::move(classDef)); } } - *out << "}\n"; + if (!ClassDefinition::writeJavaFile(&rClass, util::utf16ToUtf8(outPackageName), + mOptions.useFinal, out)) { + return false; + } + out->flush(); return true; } diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h index 7e46f8c9043c..b594a88728f4 100644 --- a/tools/aapt2/java/JavaClassGenerator.h +++ b/tools/aapt2/java/JavaClassGenerator.h @@ -28,7 +28,7 @@ namespace aapt { class AnnotationProcessor; -class ClassDefinitionWriter; +class ClassDefinition; struct JavaClassGeneratorOptions { /* @@ -70,16 +70,15 @@ public: const std::string& getError() const; private: - bool writeEntriesForClass(ClassDefinitionWriter* outClassDef, - const StringPiece16& packageNameToGenerate, - const ResourceTablePackage* package, - const ResourceTableType* type); - - void writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef, - AnnotationProcessor* processor, - const StringPiece16& packageNameToGenerate, - const std::u16string& entryName, - const Styleable* styleable); + bool addMembersToTypeClass(const StringPiece16& packageNameToGenerate, + const ResourceTablePackage* package, + const ResourceTableType* type, + ClassDefinition* outTypeClassDef); + + void addMembersToStyleableClass(const StringPiece16& packageNameToGenerate, + const std::u16string& entryName, + const Styleable* styleable, + ClassDefinition* outStyleableClassDef); bool skipSymbol(SymbolState state); diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp index a9b4c14337fe..be8955ecdf83 100644 --- a/tools/aapt2/java/ManifestClassGenerator.cpp +++ b/tools/aapt2/java/ManifestClassGenerator.cpp @@ -16,7 +16,7 @@ #include "Source.h" #include "java/AnnotationProcessor.h" -#include "java/ClassDefinitionWriter.h" +#include "java/ClassDefinition.h" #include "java/ManifestClassGenerator.h" #include "util/Maybe.h" #include "xml/XmlDom.h" @@ -58,8 +58,8 @@ static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Sour return result; } -static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, const Source& source, - xml::Element* el) { +static bool writeSymbol(const Source& source, IDiagnostics* diag, xml::Element* el, + ClassDefinition* classDef) { xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name"); if (!attr) { diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'"); @@ -72,54 +72,53 @@ static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, return false; } - AnnotationProcessor processor; - processor.appendComment(el->comment); - outClassDef->addStringMember(result.value(), &processor, attr->value); + std::unique_ptr<StringMember> stringMember = util::make_unique<StringMember>( + util::utf16ToUtf8(result.value()), util::utf16ToUtf8(attr->value)); + stringMember->getCommentBuilder()->appendComment(el->comment); + + classDef->addMember(std::move(stringMember)); return true; } -bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package, - xml::XmlResource* res, std::ostream* out) { +std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res) { xml::Element* el = xml::findRootElement(res->root.get()); if (!el) { - return false; + diag->error(DiagMessage(res->file.source) << "no root tag defined"); + return {}; } if (el->name != u"manifest" && !el->namespaceUri.empty()) { diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined"); - return false; + return {}; } - *out << "package " << package << ";\n\n" - << "public final class Manifest {\n"; + std::unique_ptr<ClassDefinition> permissionClass = + util::make_unique<ClassDefinition>("permission", ClassQualifier::Static, false); + std::unique_ptr<ClassDefinition> permissionGroupClass = + util::make_unique<ClassDefinition>("permission_group", ClassQualifier::Static, false); bool error = false; - std::vector<xml::Element*> children = el->getChildElements(); - ClassDefinitionWriterOptions classOptions; - classOptions.useFinalQualifier = true; - classOptions.forceCreationIfEmpty = false; - - // First write out permissions. - ClassDefinitionWriter classDef("permission", classOptions); + std::vector<xml::Element*> children = el->getChildElements(); for (xml::Element* childEl : children) { - if (childEl->namespaceUri.empty() && childEl->name == u"permission") { - error |= !writeSymbol(diag, &classDef, res->file.source, childEl); + if (childEl->namespaceUri.empty()) { + if (childEl->name == u"permission") { + error |= !writeSymbol(res->file.source, diag, childEl, permissionClass.get()); + } else if (childEl->name == u"permission-group") { + error |= !writeSymbol(res->file.source, diag, childEl, permissionGroupClass.get()); + } } } - classDef.writeToStream(out, " "); - // Next write out permission groups. - classDef = ClassDefinitionWriter("permission_group", classOptions); - for (xml::Element* childEl : children) { - if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") { - error |= !writeSymbol(diag, &classDef, res->file.source, childEl); - } + if (error) { + return {}; } - classDef.writeToStream(out, " "); - *out << "}\n"; - return !error; + std::unique_ptr<ClassDefinition> manifestClass = + util::make_unique<ClassDefinition>("Manifest", ClassQualifier::None, false); + manifestClass->addMember(std::move(permissionClass)); + manifestClass->addMember(std::move(permissionGroupClass)); + return manifestClass; } } // namespace aapt diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h index 226ed23b85f8..f565289393fb 100644 --- a/tools/aapt2/java/ManifestClassGenerator.h +++ b/tools/aapt2/java/ManifestClassGenerator.h @@ -18,6 +18,7 @@ #define AAPT_JAVA_MANIFESTCLASSGENERATOR_H #include "Diagnostics.h" +#include "java/ClassDefinition.h" #include "util/StringPiece.h" #include "xml/XmlDom.h" @@ -25,10 +26,7 @@ namespace aapt { -struct ManifestClassGenerator { - bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res, - std::ostream* out); -}; +std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res); } // namespace aapt diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp index fc57ae6fd8ff..a9ec3189ec4f 100644 --- a/tools/aapt2/java/ManifestClassGenerator_test.cpp +++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp @@ -22,6 +22,23 @@ namespace aapt { +static ::testing::AssertionResult getManifestClassText(IAaptContext* context, xml::XmlResource* res, + std::string* outStr) { + std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass( + context->getDiagnostics(), res); + if (!manifestClass) { + return ::testing::AssertionFailure() << "manifestClass == nullptr"; + } + + std::stringstream out; + if (!manifestClass->writeJavaFile(manifestClass.get(), "android", true, &out)) { + return ::testing::AssertionFailure() << "failed to write java file"; + } + + *outStr = out.str(); + return ::testing::AssertionSuccess(); +} + TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF( @@ -32,11 +49,8 @@ TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) { <permission-group android:name="foo.bar.PERMISSION" /> </manifest>)EOF"); - std::stringstream out; - ManifestClassGenerator generator; - ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out)); - - std::string actual = out.str(); + std::string actual; + ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual)); const size_t permissionClassPos = actual.find("public static final class permission {"); const size_t permissionGroupClassPos = @@ -87,11 +101,8 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) { <permission android:name="android.permission.SECRET" /> </manifest>)EOF"); - std::stringstream out; - ManifestClassGenerator generator; - ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out)); - - std::string actual = out.str(); + std::string actual; + ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual)); EXPECT_NE(std::string::npos, actual.find( R"EOF( /** diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index b84074d1cb58..8c10fbb6e38a 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -762,9 +762,24 @@ public: return true; } + std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass( + mContext->getDiagnostics(), manifestXml); + + if (!manifestClass) { + // Something bad happened, but we already logged it, so exit. + return false; + } + + if (manifestClass->empty()) { + // Empty Manifest class, no need to generate it. + return true; + } + + const std::string packageUtf8 = util::utf16ToUtf8(mContext->getCompilationPackage()); + std::string outPath = mOptions.generateJavaClassPath.value(); - file::appendPath(&outPath, - file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage()))); + file::appendPath(&outPath, file::packageToPath(packageUtf8)); + if (!file::mkdirs(outPath)) { mContext->getDiagnostics()->error( DiagMessage() << "failed to create directory '" << outPath << "'"); @@ -780,13 +795,7 @@ public: return false; } - ManifestClassGenerator generator; - if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(), - manifestXml, &fout)) { - return false; - } - - if (!fout) { + if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) { mContext->getDiagnostics()->error( DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno)); return false; diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py index 75e837b43cfc..b5ed1b52a19d 100755 --- a/tools/fonts/fontchain_lint.py +++ b/tools/fonts/fontchain_lint.py @@ -177,9 +177,10 @@ def parse_fonts_xml(fonts_xml_path): def check_emoji_availability(): emoji_fonts = [font[5] for font in _fallback_chain if 'Zsye' in font[1]] + assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts) + emoji_font = emoji_fonts[0] emoji_chars = _emoji_properties['Emoji'] - for emoji_font in emoji_fonts: - assert_font_supports_all_of_chars(emoji_font, emoji_chars) + assert_font_supports_all_of_chars(emoji_font, emoji_chars) def check_emoji_defaults(): @@ -273,11 +274,12 @@ def main(): hyphens_dir = path.join(target_out, 'usr', 'hyphen-data') check_hyphens(hyphens_dir) - ucd_path = sys.argv[2] - parse_ucd(ucd_path) - # Temporarily disable emoji checks for Bug 27785690 - # check_emoji_availability() - # check_emoji_defaults() + check_emoji = sys.argv[2] + if check_emoji == 'true': + ucd_path = sys.argv[3] + parse_ucd(ucd_path) + check_emoji_availability() + check_emoji_defaults() if __name__ == '__main__': diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 7faee1b3d174..f9e008e1cf92 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -403,8 +403,15 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void setNewConfiguration(Configuration arg0) throws RemoteException { + public int[] setNewConfiguration(Configuration arg0) throws RemoteException { // TODO Auto-generated method stub + return null; + } + + @Override + public Rect getBoundsForNewConfiguration(int stackId) throws RemoteException { + // TODO Auto-generated method stub + return null; } @Override |