diff options
351 files changed, 12501 insertions, 4743 deletions
diff --git a/api/current.txt b/api/current.txt index da601e1519a8..391161660719 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1710,6 +1710,7 @@ package android { field public static final int ic_notification_clear_all = 17301594; // 0x108005a field public static final int ic_notification_overlay = 17301595; // 0x108005b field public static final int ic_partial_secure = 17301596; // 0x108005c + field public static final int ic_picture_in_picture = 17301685; // 0x10800b5 field public static final int ic_popup_disk_full = 17301597; // 0x108005d field public static final int ic_popup_reminder = 17301598; // 0x108005e field public static final int ic_popup_sync = 17301599; // 0x108005f @@ -3848,7 +3849,7 @@ package android.app { method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException; method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses(); method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException; - method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; + method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException; method public deprecated boolean isInLockTaskMode(); method public boolean isLowRamDevice(); @@ -4577,6 +4578,7 @@ package android.app { method public final android.app.FragmentManager getFragmentManager(); method public final java.lang.Object getHost(); method public final int getId(); + method public final android.view.LayoutInflater getLayoutInflater(); method public android.app.LoaderManager getLoaderManager(); method public final android.app.Fragment getParentFragment(); method public android.transition.Transition getReenterTransition(); @@ -5604,7 +5606,6 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -6625,7 +6626,7 @@ package android.app.assist { public static class AssistStructure.ViewNode { method public float getAlpha(); - method public java.lang.String[] getAutoFillHints(); + method public java.lang.String[] getAutofillHints(); method public android.view.autofill.AutofillId getAutofillId(); method public java.lang.String[] getAutofillOptions(); method public int getAutofillType(); @@ -6890,7 +6891,7 @@ package android.app.job { } public abstract class JobServiceEngine { - ctor public JobServiceEngine(android.content.Context); + ctor public JobServiceEngine(android.app.Service); method public final android.os.IBinder getBinder(); method public final void jobFinished(android.app.job.JobParameters, boolean); method public abstract boolean onStartJob(android.app.job.JobParameters); @@ -6994,12 +6995,12 @@ package android.app.usage { } public class StorageStatsManager { - method public long getFreeBytes(java.lang.String); - method public long getTotalBytes(java.lang.String); - method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle); - method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle); - method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int); - method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle); + method public long getFreeBytes(java.util.UUID) throws java.io.IOException; + method public long getTotalBytes(java.util.UUID) throws java.io.IOException; + method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException; + method public android.app.usage.StorageStats queryStatsForPackage(java.util.UUID, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method public android.app.usage.StorageStats queryStatsForUid(java.util.UUID, int) throws java.io.IOException; + method public android.app.usage.StorageStats queryStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException; } public final class UsageEvents implements android.os.Parcelable { @@ -7064,6 +7065,7 @@ package android.appwidget { method public static void deleteAllHosts(); method public void deleteAppWidgetId(int); method public void deleteHost(); + method public int[] getAppWidgetIds(); method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo); method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo); method protected void onProvidersChanged(); @@ -9382,7 +9384,6 @@ package android.content { field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; - field public static final java.lang.String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED"; field public static final java.lang.String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; field public static final java.lang.String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED"; field public static final deprecated java.lang.String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; @@ -9892,7 +9893,7 @@ package android.content { } public abstract interface ServiceConnection { - method public default void onBindingDead(android.content.ComponentName); + method public default void onBindingDied(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } @@ -10217,12 +10218,12 @@ package android.content.pm { field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; + field public java.util.UUID storageUuid; field public int targetSdkVersion; field public java.lang.String taskAffinity; field public int theme; field public int uiOptions; field public int uid; - field public java.lang.String volumeUuid; } public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator { @@ -10341,7 +10342,7 @@ package android.content.pm { public class LauncherApps { method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle); - method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle); + method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent); method public java.util.List<android.os.UserHandle> getProfiles(); method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); @@ -10570,6 +10571,7 @@ package android.content.pm { method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); method public abstract int checkSignatures(int, int); + method public abstract void clearInstantAppCookie(); method public abstract void clearPackagePreferredActivities(java.lang.String); method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public abstract void extendVerificationTimeout(int, int, long); @@ -10598,7 +10600,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public abstract java.lang.String getInstallerPackageName(java.lang.String); method public abstract byte[] getInstantAppCookie(); - method public abstract int getInstantAppCookieMaxSize(); + method public abstract int getInstantAppCookieMaxBytes(); method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String); method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); @@ -10653,7 +10655,7 @@ package android.content.pm { method public abstract void setApplicationEnabledSetting(java.lang.String, int, int); method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); - method public abstract boolean setInstantAppCookie(byte[]); + method public abstract void updateInstantAppCookie(byte[]); method public abstract void verifyPendingInstall(int, int); field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0 field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2 @@ -10941,12 +10943,13 @@ package android.content.pm { method public android.content.pm.VersionedPackage getDeclaringPackage(); method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); method public java.lang.String getName(); - method public int getVersion(); - method public boolean isBuiltin(); - method public boolean isDynamic(); - method public boolean isStatic(); + method public int getType(); + method public long getVersion(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR; + field public static final int TYPE_BUILTIN = 0; // 0x0 + field public static final int TYPE_DYNAMIC = 1; // 0x1 + field public static final int TYPE_STATIC = 2; // 0x2 field public static final int VERSION_UNDEFINED = -1; // 0xffffffff } @@ -12600,7 +12603,6 @@ package android.graphics { public class BitmapShader extends android.graphics.Shader { ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); - method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); } public class BlurMaskFilter extends android.graphics.MaskFilter { @@ -12853,8 +12855,6 @@ package android.graphics { ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix); ctor public ColorMatrixColorFilter(float[]); method public void getColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrixArray(float[]); } public abstract class ColorSpace { @@ -12995,8 +12995,6 @@ package android.graphics { public class ComposeShader extends android.graphics.Shader { ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -13069,15 +13067,11 @@ package android.graphics { ctor public LightingColorFilter(int, int); method public int getColorAdd(); method public int getColorMultiply(); - method public void setColorAdd(int); - method public void setColorMultiply(int); } public class LinearGradient extends android.graphics.Shader { ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode); } public class MaskFilter { @@ -13587,10 +13581,6 @@ package android.graphics { public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode); - method public int getColor(); - method public android.graphics.PorterDuff.Mode getMode(); - method public void setColor(int); - method public void setMode(android.graphics.PorterDuff.Mode); } public class PorterDuffXfermode extends android.graphics.Xfermode { @@ -13600,8 +13590,6 @@ package android.graphics { public class RadialGradient extends android.graphics.Shader { ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, int, int, android.graphics.Shader.TileMode); } public final class Rect implements android.os.Parcelable { @@ -13786,8 +13774,6 @@ package android.graphics { public class SweepGradient extends android.graphics.Shader { ctor public SweepGradient(float, float, int[], float[]); ctor public SweepGradient(float, float, int, int); - method public void set(float, float, int[], float[]); - method public void set(float, float, int, int); } public class Typeface { @@ -14833,7 +14819,7 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light"; field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration"; - field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody"; + field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody_detect"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated"; field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; @@ -20801,13 +20787,10 @@ package android.location { method public boolean hasSpeedAccuracy(); method public boolean hasVerticalAccuracy(); method public boolean isFromMockProvider(); - method public void removeAccuracy(); - method public void removeAltitude(); - method public void removeBearing(); - method public void removeBearingAccuracy(); - method public void removeSpeed(); - method public void removeSpeedAccuracy(); - method public void removeVerticalAccuracy(); + method public deprecated void removeAccuracy(); + method public deprecated void removeAltitude(); + method public deprecated void removeBearing(); + method public deprecated void removeSpeed(); method public void reset(); method public void set(android.location.Location); method public void setAccuracy(float); @@ -22775,7 +22758,7 @@ package android.media { method public android.media.MediaPlayer.DrmInfo getDrmInfo(); method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException; method public int getDuration(); - method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; + method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -22789,7 +22772,7 @@ package android.media { method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void prepareAsync() throws java.lang.IllegalStateException; - method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; + method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException; method public void release(); method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException; @@ -22816,7 +22799,7 @@ package android.media { method public void setNextMediaPlayer(android.media.MediaPlayer); method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener); method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener); - method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener); + method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler); method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener); @@ -22857,6 +22840,10 @@ package android.media { field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3 field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2 + field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0 field public static final int SEEK_CLOSEST = 3; // 0x3 field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_NEXT_SYNC = 1; // 0x1 @@ -22866,7 +22853,6 @@ package android.media { } public static final class MediaPlayer.DrmInfo { - method public java.lang.String[] getMimes(); method public java.util.Map<java.util.UUID, byte[]> getPssh(); method public java.util.UUID[] getSupportedSchemes(); } @@ -22898,7 +22884,7 @@ package android.media { method public abstract void onCompletion(android.media.MediaPlayer); } - public static abstract interface MediaPlayer.OnDrmConfigListener { + public static abstract interface MediaPlayer.OnDrmConfigHelper { method public abstract void onDrmConfig(android.media.MediaPlayer); } @@ -22907,7 +22893,7 @@ package android.media { } public static abstract interface MediaPlayer.OnDrmPreparedListener { - method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean); + method public abstract void onDrmPrepared(android.media.MediaPlayer, int); } public static abstract interface MediaPlayer.OnErrorListener { @@ -22938,8 +22924,12 @@ package android.media { method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int); } - public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException { - ctor public MediaPlayer.ProvisioningErrorException(java.lang.String); + public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String); + } + + public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String); } public static class MediaPlayer.TrackInfo implements android.os.Parcelable { @@ -24011,7 +24001,6 @@ package android.media.browse { method public android.content.ComponentName getServiceComponent(); method public android.media.session.MediaSession.Token getSessionToken(); method public boolean isConnected(); - method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback); method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback); method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback); method public void unsubscribe(java.lang.String); @@ -24047,12 +24036,6 @@ package android.media.browse { field public static final int FLAG_PLAYABLE = 2; // 0x2 } - public static abstract class MediaBrowser.SearchCallback { - ctor public MediaBrowser.SearchCallback(); - method public void onError(java.lang.String, android.os.Bundle); - method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>); - } - public static abstract class MediaBrowser.SubscriptionCallback { ctor public MediaBrowser.SubscriptionCallback(); method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>); @@ -24261,8 +24244,6 @@ package android.media.session { public final class MediaController { ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token); - method public void addQueueItem(android.media.MediaDescription); - method public void addQueueItem(android.media.MediaDescription, int); method public void adjustVolume(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public android.os.Bundle getExtras(); @@ -24274,15 +24255,11 @@ package android.media.session { method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue(); method public java.lang.CharSequence getQueueTitle(); method public int getRatingType(); - method public int getRepeatMode(); method public android.app.PendingIntent getSessionActivity(); method public android.media.session.MediaSession.Token getSessionToken(); method public android.media.session.MediaController.TransportControls getTransportControls(); - method public boolean isShuffleModeEnabled(); method public void registerCallback(android.media.session.MediaController.Callback); method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler); - method public void removeQueueItem(android.media.MediaDescription); - method public void removeQueueItemAt(int); method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void setVolumeTo(int, int); method public void unregisterCallback(android.media.session.MediaController.Callback); @@ -24296,10 +24273,8 @@ package android.media.session { method public void onPlaybackStateChanged(android.media.session.PlaybackState); method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>); method public void onQueueTitleChanged(java.lang.CharSequence); - method public void onRepeatModeChanged(int); method public void onSessionDestroyed(); method public void onSessionEvent(java.lang.String, android.os.Bundle); - method public void onShuffleModeChanged(boolean); } public static final class MediaController.PlaybackInfo { @@ -24328,8 +24303,6 @@ package android.media.session { method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle); method public void sendCustomAction(java.lang.String, android.os.Bundle); method public void setRating(android.media.Rating); - method public void setRepeatMode(int); - method public void setShuffleModeEnabled(boolean); method public void skipToNext(); method public void skipToPrevious(); method public void skipToQueueItem(long); @@ -24356,18 +24329,13 @@ package android.media.session { method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>); method public void setQueueTitle(java.lang.CharSequence); method public void setRatingType(int); - method public void setRepeatMode(int); method public void setSessionActivity(android.app.PendingIntent); - method public void setShuffleModeEnabled(boolean); field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 - field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4 field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class MediaSession.Callback { ctor public MediaSession.Callback(); - method public void onAddQueueItem(android.media.MediaDescription); - method public void onAddQueueItem(android.media.MediaDescription, int); method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void onCustomAction(java.lang.String, android.os.Bundle); method public void onFastForward(); @@ -24381,13 +24349,9 @@ package android.media.session { method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle); method public void onPrepareFromSearch(java.lang.String, android.os.Bundle); method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); - method public void onRemoveQueueItem(android.media.MediaDescription); - method public void onRemoveQueueItemAt(int); method public void onRewind(); method public void onSeekTo(long); method public void onSetRating(android.media.Rating); - method public void onSetRepeatMode(int); - method public void onSetShuffleModeEnabled(boolean); method public void onSkipToNext(); method public void onSkipToPrevious(); method public void onSkipToQueueItem(long); @@ -24448,17 +24412,12 @@ package android.media.session { field public static final long ACTION_REWIND = 8L; // 0x8L field public static final long ACTION_SEEK_TO = 256L; // 0x100L field public static final long ACTION_SET_RATING = 128L; // 0x80L - field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L - field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L field public static final long ACTION_STOP = 1L; // 0x1L field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR; field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL - field public static final int REPEAT_MODE_ALL = 2; // 0x2 - field public static final int REPEAT_MODE_NONE = 0; // 0x0 - field public static final int REPEAT_MODE_ONE = 1; // 0x1 field public static final int STATE_BUFFERING = 6; // 0x6 field public static final int STATE_CONNECTING = 8; // 0x8 field public static final int STATE_ERROR = 7; // 0x7 @@ -25559,14 +25518,10 @@ package android.net { } public final class IpSecManager { - method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException; method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform); - method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform); - method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform); + method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 @@ -25585,7 +25540,7 @@ package android.net { } public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public void close(); + method public void close() throws java.io.IOException; method public int getPort(); method public java.io.FileDescriptor getSocket(); } @@ -26783,8 +26738,8 @@ package android.net.wifi.aware { } public class DiscoverySession { - method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); - method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); method public void destroy(); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -26870,8 +26825,8 @@ package android.net.wifi.aware { } public class WifiAwareSession { - method public java.lang.String createNetworkSpecifierOpen(int, byte[]); - method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String); method public void destroy(); method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); @@ -30802,6 +30757,7 @@ package android.os { method public android.util.SizeF getSizeF(java.lang.String); method public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(java.lang.String); method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String); + method public java.util.UUID getUuid(java.lang.String); method public boolean hasFileDescriptors(); method public void putAll(android.os.Bundle); method public void putBinder(java.lang.String, android.os.IBinder); @@ -30826,6 +30782,7 @@ package android.os { method public void putSizeF(java.lang.String, android.util.SizeF); method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>); method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>); + method public void putUuid(java.lang.String, java.util.UUID); method public void readFromParcel(android.os.Parcel); method public void setClassLoader(java.lang.ClassLoader); method public void writeToParcel(android.os.Parcel, int); @@ -31355,6 +31312,7 @@ package android.os { method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>); method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>); method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>); + method public final java.util.UUID readUuid(); method public final java.lang.Object readValue(java.lang.ClassLoader); method public final void recycle(); method public final void setDataCapacity(int); @@ -31400,6 +31358,7 @@ package android.os { method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int); method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>); method public final <T extends android.os.Parcelable> void writeTypedObject(T, int); + method public final void writeUuid(java.util.UUID); method public final void writeValue(java.lang.Object); field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR; } @@ -32030,15 +31989,16 @@ package android.os.storage { } public class StorageManager { - method public void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException; method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException; - method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException; - method public long getCacheQuotaBytes(java.io.File); - method public long getCacheSizeBytes(java.io.File); + method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException; + method public long getCacheQuotaBytes(java.util.UUID) throws java.io.IOException; + method public long getCacheSizeBytes(java.util.UUID) throws java.io.IOException; method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryStorageVolume(); method public android.os.storage.StorageVolume getStorageVolume(java.io.File); method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); + method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException; method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException; method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException; method public boolean isEncrypted(java.io.File); @@ -32050,7 +32010,10 @@ package android.os.storage { method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + field public static final java.lang.String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; + field public static final java.lang.String EXTRA_UUID = "android.os.storage.extra.UUID"; field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 + field public static final java.util.UUID UUID_DEFAULT; } public final class StorageVolume implements android.os.Parcelable { @@ -35487,6 +35450,16 @@ package android.provider { field public static final java.lang.String SUBSCRIPTION_ID = "pending_sub_id"; } + public static final class Telephony.ServiceStateTable { + method public static android.net.Uri getUriForSubId(int); + method public static android.net.Uri getUriForSubIdAndField(int, java.lang.String); + field public static final java.lang.String AUTHORITY = "service-state"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection"; + field public static final java.lang.String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; + field public static final java.lang.String VOICE_REG_STATE = "voice_reg_state"; + } + public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { method public static java.lang.String getDefaultSmsPackage(android.content.Context); field public static final android.net.Uri CONTENT_URI; @@ -37061,6 +37034,7 @@ package android.service.autofill { public abstract class AutofillService extends android.app.Service { ctor public AutofillService(); method public final deprecated void disableSelf(); + method public final android.service.autofill.FillEventHistory getFillEventHistory(); method public final android.os.IBinder onBind(android.content.Intent); method public void onConnected(); method public void onDisconnected(); @@ -37083,6 +37057,7 @@ package android.service.autofill { ctor public Dataset.Builder(); method public android.service.autofill.Dataset build(); method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.Dataset.Builder setId(java.lang.String); method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue); method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews); } @@ -37100,6 +37075,23 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR; } + public final class FillEventHistory implements android.os.Parcelable { + method public int describeContents(); + method public android.os.Bundle getClientState(); + method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR; + } + + public static final class FillEventHistory.Event { + method public java.lang.String getDatasetId(); + method public int getType(); + field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2 + field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1 + field public static final int TYPE_DATASET_SELECTED = 0; // 0x0 + field public static final int TYPE_SAVE_SHOWN = 3; // 0x3 + } + public final class FillRequest implements android.os.Parcelable { method public int describeContents(); method public android.os.Bundle getClientState(); @@ -37124,6 +37116,7 @@ package android.service.autofill { method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews); method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle); method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); + method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...); method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo); } @@ -37136,6 +37129,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; + field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 @@ -37148,6 +37142,7 @@ package android.service.autofill { ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]); method public android.service.autofill.SaveInfo build(); method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + method public android.service.autofill.SaveInfo.Builder setFlags(int); method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender); method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]); } @@ -37341,7 +37336,6 @@ package android.service.media { method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>); method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle); method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>); - method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>); method public void setSessionToken(android.media.session.MediaSession.Token); field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; } @@ -37421,16 +37415,16 @@ package android.service.notification { method public final int getCurrentInterruptionFilter(); method public final int getCurrentListenerHints(); method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); - method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String); - method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); + method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String, android.os.UserHandle); + method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String, android.os.UserHandle); method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications(); method public android.os.IBinder onBind(android.content.Intent); method public void onInterruptionFilterChanged(int); method public void onListenerConnected(); method public void onListenerDisconnected(); method public void onListenerHintsChanged(int); - method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int); - method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int); + method public void onNotificationChannelGroupModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannelGroup, int); + method public void onNotificationChannelModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannel, int); method public void onNotificationPosted(android.service.notification.StatusBarNotification); method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); @@ -37443,7 +37437,7 @@ package android.service.notification { method public final void requestUnbind(); method public final void setNotificationsShown(java.lang.String[]); method public final void snoozeNotification(java.lang.String, long); - method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); + method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel); field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4 field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2 @@ -40919,7 +40913,6 @@ package android.test.mock { method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; method public android.content.ComponentName startService(android.content.Intent); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean stopService(android.content.Intent); method public void unbindService(android.content.ServiceConnection); method public void unregisterReceiver(android.content.BroadcastReceiver); @@ -40987,6 +40980,7 @@ package android.test.mock { method public int checkPermission(java.lang.String, java.lang.String); method public int checkSignatures(java.lang.String, java.lang.String); method public int checkSignatures(int, int); + method public void clearInstantAppCookie(); method public void clearPackagePreferredActivities(java.lang.String); method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public void extendVerificationTimeout(int, int, long); @@ -41016,7 +41010,7 @@ package android.test.mock { method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public java.lang.String getInstallerPackageName(java.lang.String); method public byte[] getInstantAppCookie(); - method public int getInstantAppCookieMaxSize(); + method public int getInstantAppCookieMaxBytes(); method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Intent getLaunchIntentForPackage(java.lang.String); method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); @@ -41070,7 +41064,7 @@ package android.test.mock { method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); - method public boolean setInstantAppCookie(byte[]); + method public void updateInstantAppCookie(byte[]); method public void verifyPendingInstall(int, int); } @@ -45953,7 +45947,9 @@ package android.view { field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0 field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2 + field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8 field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1 + field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 @@ -49733,6 +49729,7 @@ package android.widget { method public java.lang.String getFormat(); method public android.widget.Chronometer.OnChronometerTickListener getOnChronometerTickListener(); method public boolean isCountDown(); + method public boolean isTheFinalCountDown(); method public void setBase(long); method public void setCountDown(boolean); method public void setFormat(java.lang.String); @@ -55402,6 +55399,7 @@ package java.lang.invoke { method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...); method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType); + method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType); method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...); method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); diff --git a/api/removed.txt b/api/removed.txt index 1e8370e97dc5..189536d8c695 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -22,6 +22,20 @@ package android.app.admin { } +package android.app.usage { + + public class StorageStatsManager { + method public deprecated long getFreeBytes(java.lang.String) throws java.io.IOException; + method public deprecated long getTotalBytes(java.lang.String) throws java.io.IOException; + method public deprecated boolean isQuotaSupported(java.lang.String); + method public deprecated android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException; + method public deprecated android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method public deprecated android.app.usage.StorageStats queryStatsForUid(java.lang.String, int) throws java.io.IOException; + method public deprecated android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException; + } + +} + package android.content { public abstract class Context { @@ -37,6 +51,10 @@ package android.content { package android.content.pm { + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public deprecated java.lang.String volumeUuid; + } + public class ComponentInfo extends android.content.pm.PackageItemInfo { field public deprecated boolean encryptionAware; } @@ -45,6 +63,16 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 } + public abstract class PackageManager { + method public abstract boolean setInstantAppCookie(byte[]); + } + + public final class SharedLibraryInfo implements android.os.Parcelable { + method public boolean isBuiltin(); + method public boolean isDynamic(); + method public boolean isStatic(); + } + } package android.database { @@ -108,6 +136,16 @@ package android.hardware { } +package android.location { + + public class Location implements android.os.Parcelable { + method public deprecated void removeBearingAccuracy(); + method public deprecated void removeSpeedAccuracy(); + method public deprecated void removeVerticalAccuracy(); + } + +} + package android.media { public final class AudioFormat implements android.os.Parcelable { @@ -180,10 +218,14 @@ package android.os { package android.os.storage { public class StorageManager { - method public deprecated long getCacheQuotaBytes(); - method public deprecated long getCacheSizeBytes(); - method public deprecated long getExternalCacheQuotaBytes(); - method public deprecated long getExternalCacheSizeBytes(); + method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException; + method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException; + method public deprecated long getCacheQuotaBytes() throws java.io.IOException; + method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException; + method public deprecated long getCacheSizeBytes() throws java.io.IOException; + method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException; + method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException; method public android.os.storage.StorageVolume getPrimaryVolume(); method public android.os.storage.StorageVolume[] getVolumeList(); method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException; diff --git a/api/system-current.txt b/api/system-current.txt index 21fce319ebe9..331aa94afa89 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1197,6 +1197,8 @@ package android { field public static final int requiredFeature = 16844119; // 0x1010557 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 field public static final int requiredNotFeature = 16844120; // 0x1010558 + field public static final int requiredSystemPropertyName = 16844136; // 0x1010568 + field public static final int requiredSystemPropertyValue = 16844137; // 0x1010569 field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeClip = 16843983; // 0x10104cf @@ -1832,6 +1834,7 @@ package android { field public static final int ic_notification_clear_all = 17301594; // 0x108005a field public static final int ic_notification_overlay = 17301595; // 0x108005b field public static final int ic_partial_secure = 17301596; // 0x108005c + field public static final int ic_picture_in_picture = 17301685; // 0x10800b5 field public static final int ic_popup_disk_full = 17301597; // 0x108005d field public static final int ic_popup_reminder = 17301598; // 0x108005e field public static final int ic_popup_sync = 17301599; // 0x108005f @@ -3987,7 +3990,7 @@ package android.app { method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException; method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses(); method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException; - method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; + method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException; method public int getUidImportance(int); method public deprecated boolean isInLockTaskMode(); @@ -4742,6 +4745,7 @@ package android.app { method public final android.app.FragmentManager getFragmentManager(); method public final java.lang.Object getHost(); method public final int getId(); + method public final android.view.LayoutInflater getLayoutInflater(); method public android.app.LoaderManager getLoaderManager(); method public final android.app.Fragment getParentFragment(); method public android.transition.Transition getReenterTransition(); @@ -5804,7 +5808,6 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -6868,7 +6871,7 @@ package android.app.assist { public static class AssistStructure.ViewNode { method public float getAlpha(); - method public java.lang.String[] getAutoFillHints(); + method public java.lang.String[] getAutofillHints(); method public android.view.autofill.AutofillId getAutofillId(); method public java.lang.String[] getAutofillOptions(); method public int getAutofillType(); @@ -7324,7 +7327,7 @@ package android.app.job { } public abstract class JobServiceEngine { - ctor public JobServiceEngine(android.content.Context); + ctor public JobServiceEngine(android.app.Service); method public final android.os.IBinder getBinder(); method public final void jobFinished(android.app.job.JobParameters, boolean); method public abstract boolean onStartJob(android.app.job.JobParameters); @@ -7457,12 +7460,12 @@ package android.app.usage { } public class StorageStatsManager { - method public long getFreeBytes(java.lang.String); - method public long getTotalBytes(java.lang.String); - method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle); - method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle); - method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int); - method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle); + method public long getFreeBytes(java.util.UUID) throws java.io.IOException; + method public long getTotalBytes(java.util.UUID) throws java.io.IOException; + method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException; + method public android.app.usage.StorageStats queryStatsForPackage(java.util.UUID, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method public android.app.usage.StorageStats queryStatsForUid(java.util.UUID, int) throws java.io.IOException; + method public android.app.usage.StorageStats queryStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException; } public final class UsageEvents implements android.os.Parcelable { @@ -7528,6 +7531,7 @@ package android.appwidget { method public static void deleteAllHosts(); method public void deleteAppWidgetId(int); method public void deleteHost(); + method public int[] getAppWidgetIds(); method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo); method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo); method protected void onProvidersChanged(); @@ -9912,7 +9916,6 @@ package android.content { field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; - field public static final java.lang.String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED"; field public static final java.lang.String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; field public static final java.lang.String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED"; field public static final deprecated java.lang.String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; @@ -10464,7 +10467,7 @@ package android.content { } public abstract interface ServiceConnection { - method public default void onBindingDead(android.content.ComponentName); + method public default void onBindingDied(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } @@ -10790,12 +10793,12 @@ package android.content.pm { field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; + field public java.util.UUID storageUuid; field public int targetSdkVersion; field public java.lang.String taskAffinity; field public int theme; field public int uiOptions; field public int uid; - field public java.lang.String volumeUuid; } public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator { @@ -11004,7 +11007,7 @@ package android.content.pm { public class LauncherApps { method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle); - method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle); + method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent); method public java.util.List<android.os.UserHandle> getProfiles(); method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); @@ -11241,6 +11244,7 @@ package android.content.pm { method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); method public abstract int checkSignatures(int, int); + method public abstract void clearInstantAppCookie(); method public abstract void clearPackagePreferredActivities(java.lang.String); method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public abstract void extendVerificationTimeout(int, int, long); @@ -11272,7 +11276,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method public abstract java.lang.String getInstallerPackageName(java.lang.String); method public abstract byte[] getInstantAppCookie(); - method public abstract int getInstantAppCookieMaxSize(); + method public abstract int getInstantAppCookieMaxBytes(); method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String); method public abstract android.content.ComponentName getInstantAppInstallerComponent(); method public abstract android.content.ComponentName getInstantAppResolverSettingsComponent(); @@ -11339,8 +11343,8 @@ package android.content.pm { method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); - method public abstract boolean setInstantAppCookie(byte[]); method public abstract void setUpdateAvailable(java.lang.String, boolean); + method public abstract void updateInstantAppCookie(byte[]); method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int); method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle); method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>); @@ -11699,12 +11703,13 @@ package android.content.pm { method public android.content.pm.VersionedPackage getDeclaringPackage(); method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); method public java.lang.String getName(); - method public int getVersion(); - method public boolean isBuiltin(); - method public boolean isDynamic(); - method public boolean isStatic(); + method public int getType(); + method public long getVersion(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR; + field public static final int TYPE_BUILTIN = 0; // 0x0 + field public static final int TYPE_DYNAMIC = 1; // 0x1 + field public static final int TYPE_STATIC = 2; // 0x2 field public static final int VERSION_UNDEFINED = -1; // 0xffffffff } @@ -13372,7 +13377,6 @@ package android.graphics { public class BitmapShader extends android.graphics.Shader { ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); - method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); } public class BlurMaskFilter extends android.graphics.MaskFilter { @@ -13625,8 +13629,6 @@ package android.graphics { ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix); ctor public ColorMatrixColorFilter(float[]); method public void getColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrixArray(float[]); } public abstract class ColorSpace { @@ -13767,8 +13769,6 @@ package android.graphics { public class ComposeShader extends android.graphics.Shader { ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -13841,15 +13841,11 @@ package android.graphics { ctor public LightingColorFilter(int, int); method public int getColorAdd(); method public int getColorMultiply(); - method public void setColorAdd(int); - method public void setColorMultiply(int); } public class LinearGradient extends android.graphics.Shader { ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode); } public class MaskFilter { @@ -14359,10 +14355,6 @@ package android.graphics { public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode); - method public int getColor(); - method public android.graphics.PorterDuff.Mode getMode(); - method public void setColor(int); - method public void setMode(android.graphics.PorterDuff.Mode); } public class PorterDuffXfermode extends android.graphics.Xfermode { @@ -14372,8 +14364,6 @@ package android.graphics { public class RadialGradient extends android.graphics.Shader { ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, int, int, android.graphics.Shader.TileMode); } public final class Rect implements android.os.Parcelable { @@ -14558,8 +14548,6 @@ package android.graphics { public class SweepGradient extends android.graphics.Shader { ctor public SweepGradient(float, float, int[], float[]); ctor public SweepGradient(float, float, int, int); - method public void set(float, float, int[], float[]); - method public void set(float, float, int, int); } public class Typeface { @@ -15608,7 +15596,7 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light"; field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration"; - field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody"; + field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody_detect"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated"; field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; @@ -22533,13 +22521,10 @@ package android.location { method public boolean isComplete(); method public boolean isFromMockProvider(); method public void makeComplete(); - method public void removeAccuracy(); - method public void removeAltitude(); - method public void removeBearing(); - method public void removeBearingAccuracy(); - method public void removeSpeed(); - method public void removeSpeedAccuracy(); - method public void removeVerticalAccuracy(); + method public deprecated void removeAccuracy(); + method public deprecated void removeAltitude(); + method public deprecated void removeBearing(); + method public deprecated void removeSpeed(); method public void reset(); method public void set(android.location.Location); method public void setAccuracy(float); @@ -24609,7 +24594,7 @@ package android.media { method public android.media.MediaPlayer.DrmInfo getDrmInfo(); method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException; method public int getDuration(); - method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; + method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -24623,7 +24608,7 @@ package android.media { method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void prepareAsync() throws java.lang.IllegalStateException; - method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; + method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException; method public void release(); method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException; @@ -24650,7 +24635,7 @@ package android.media { method public void setNextMediaPlayer(android.media.MediaPlayer); method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener); method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener); - method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener); + method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler); method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener); @@ -24691,6 +24676,10 @@ package android.media { field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3 field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2 + field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0 field public static final int SEEK_CLOSEST = 3; // 0x3 field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_NEXT_SYNC = 1; // 0x1 @@ -24700,7 +24689,6 @@ package android.media { } public static final class MediaPlayer.DrmInfo { - method public java.lang.String[] getMimes(); method public java.util.Map<java.util.UUID, byte[]> getPssh(); method public java.util.UUID[] getSupportedSchemes(); } @@ -24732,7 +24720,7 @@ package android.media { method public abstract void onCompletion(android.media.MediaPlayer); } - public static abstract interface MediaPlayer.OnDrmConfigListener { + public static abstract interface MediaPlayer.OnDrmConfigHelper { method public abstract void onDrmConfig(android.media.MediaPlayer); } @@ -24741,7 +24729,7 @@ package android.media { } public static abstract interface MediaPlayer.OnDrmPreparedListener { - method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean); + method public abstract void onDrmPrepared(android.media.MediaPlayer, int); } public static abstract interface MediaPlayer.OnErrorListener { @@ -24772,8 +24760,12 @@ package android.media { method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int); } - public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException { - ctor public MediaPlayer.ProvisioningErrorException(java.lang.String); + public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String); + } + + public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String); } public static class MediaPlayer.TrackInfo implements android.os.Parcelable { @@ -25931,7 +25923,6 @@ package android.media.browse { method public android.content.ComponentName getServiceComponent(); method public android.media.session.MediaSession.Token getSessionToken(); method public boolean isConnected(); - method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback); method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback); method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback); method public void unsubscribe(java.lang.String); @@ -25967,12 +25958,6 @@ package android.media.browse { field public static final int FLAG_PLAYABLE = 2; // 0x2 } - public static abstract class MediaBrowser.SearchCallback { - ctor public MediaBrowser.SearchCallback(); - method public void onError(java.lang.String, android.os.Bundle); - method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>); - } - public static abstract class MediaBrowser.SubscriptionCallback { ctor public MediaBrowser.SubscriptionCallback(); method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>); @@ -26181,8 +26166,6 @@ package android.media.session { public final class MediaController { ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token); - method public void addQueueItem(android.media.MediaDescription); - method public void addQueueItem(android.media.MediaDescription, int); method public void adjustVolume(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public android.os.Bundle getExtras(); @@ -26194,15 +26177,11 @@ package android.media.session { method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue(); method public java.lang.CharSequence getQueueTitle(); method public int getRatingType(); - method public int getRepeatMode(); method public android.app.PendingIntent getSessionActivity(); method public android.media.session.MediaSession.Token getSessionToken(); method public android.media.session.MediaController.TransportControls getTransportControls(); - method public boolean isShuffleModeEnabled(); method public void registerCallback(android.media.session.MediaController.Callback); method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler); - method public void removeQueueItem(android.media.MediaDescription); - method public void removeQueueItemAt(int); method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void setVolumeTo(int, int); method public void unregisterCallback(android.media.session.MediaController.Callback); @@ -26216,10 +26195,8 @@ package android.media.session { method public void onPlaybackStateChanged(android.media.session.PlaybackState); method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>); method public void onQueueTitleChanged(java.lang.CharSequence); - method public void onRepeatModeChanged(int); method public void onSessionDestroyed(); method public void onSessionEvent(java.lang.String, android.os.Bundle); - method public void onShuffleModeChanged(boolean); } public static final class MediaController.PlaybackInfo { @@ -26248,8 +26225,6 @@ package android.media.session { method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle); method public void sendCustomAction(java.lang.String, android.os.Bundle); method public void setRating(android.media.Rating); - method public void setRepeatMode(int); - method public void setShuffleModeEnabled(boolean); method public void skipToNext(); method public void skipToPrevious(); method public void skipToQueueItem(long); @@ -26276,18 +26251,13 @@ package android.media.session { method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>); method public void setQueueTitle(java.lang.CharSequence); method public void setRatingType(int); - method public void setRepeatMode(int); method public void setSessionActivity(android.app.PendingIntent); - method public void setShuffleModeEnabled(boolean); field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 - field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4 field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class MediaSession.Callback { ctor public MediaSession.Callback(); - method public void onAddQueueItem(android.media.MediaDescription); - method public void onAddQueueItem(android.media.MediaDescription, int); method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void onCustomAction(java.lang.String, android.os.Bundle); method public void onFastForward(); @@ -26301,13 +26271,9 @@ package android.media.session { method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle); method public void onPrepareFromSearch(java.lang.String, android.os.Bundle); method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); - method public void onRemoveQueueItem(android.media.MediaDescription); - method public void onRemoveQueueItemAt(int); method public void onRewind(); method public void onSeekTo(long); method public void onSetRating(android.media.Rating); - method public void onSetRepeatMode(int); - method public void onSetShuffleModeEnabled(boolean); method public void onSkipToNext(); method public void onSkipToPrevious(); method public void onSkipToQueueItem(long); @@ -26378,17 +26344,12 @@ package android.media.session { field public static final long ACTION_REWIND = 8L; // 0x8L field public static final long ACTION_SEEK_TO = 256L; // 0x100L field public static final long ACTION_SET_RATING = 128L; // 0x80L - field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L - field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L field public static final long ACTION_STOP = 1L; // 0x1L field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR; field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL - field public static final int REPEAT_MODE_ALL = 2; // 0x2 - field public static final int REPEAT_MODE_NONE = 0; // 0x0 - field public static final int REPEAT_MODE_ONE = 1; // 0x1 field public static final int STATE_BUFFERING = 6; // 0x6 field public static final int STATE_CONNECTING = 8; // 0x8 field public static final int STATE_ERROR = 7; // 0x7 @@ -27746,14 +27707,10 @@ package android.net { } public final class IpSecManager { - method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException; method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform); - method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform); - method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform); + method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 @@ -27772,7 +27729,7 @@ package android.net { } public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public void close(); + method public void close() throws java.io.IOException; method public int getPort(); method public java.io.FileDescriptor getSocket(); } @@ -29524,9 +29481,9 @@ package android.net.wifi.aware { } public class DiscoverySession { - method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); - method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); - method public java.lang.String createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -29612,9 +29569,9 @@ package android.net.wifi.aware { } public class WifiAwareSession { - method public java.lang.String createNetworkSpecifierOpen(int, byte[]); - method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String); - method public java.lang.String createNetworkSpecifierPmk(int, byte[], byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, byte[], byte[]); method public void destroy(); method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); @@ -33559,6 +33516,7 @@ package android.os { method public android.util.SizeF getSizeF(java.lang.String); method public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(java.lang.String); method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String); + method public java.util.UUID getUuid(java.lang.String); method public boolean hasFileDescriptors(); method public void putAll(android.os.Bundle); method public void putBinder(java.lang.String, android.os.IBinder); @@ -33583,6 +33541,7 @@ package android.os { method public void putSizeF(java.lang.String, android.util.SizeF); method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>); method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>); + method public void putUuid(java.lang.String, java.util.UUID); method public void readFromParcel(android.os.Parcel); method public void setClassLoader(java.lang.ClassLoader); method public void writeToParcel(android.os.Parcel, int); @@ -34142,6 +34101,7 @@ package android.os { method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>); method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>); method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>); + method public final java.util.UUID readUuid(); method public final java.lang.Object readValue(java.lang.ClassLoader); method public final void recycle(); method public final void setDataCapacity(int); @@ -34187,6 +34147,7 @@ package android.os { method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int); method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>); method public final <T extends android.os.Parcelable> void writeTypedObject(T, int); + method public final void writeUuid(java.util.UUID); method public final void writeValue(java.lang.Object); field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR; } @@ -34402,6 +34363,7 @@ package android.os { method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException; method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; method public static void verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File) throws java.security.GeneralSecurityException, java.io.IOException; + method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; } public static abstract interface RecoverySystem.ProgressListener { @@ -34922,15 +34884,16 @@ package android.os.storage { } public class StorageManager { - method public void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException; method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException; - method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException; - method public long getCacheQuotaBytes(java.io.File); - method public long getCacheSizeBytes(java.io.File); + method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException; + method public long getCacheQuotaBytes(java.util.UUID) throws java.io.IOException; + method public long getCacheSizeBytes(java.util.UUID) throws java.io.IOException; method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryStorageVolume(); method public android.os.storage.StorageVolume getStorageVolume(java.io.File); method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); + method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException; method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException; method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException; method public boolean isEncrypted(java.io.File); @@ -34942,7 +34905,10 @@ package android.os.storage { method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + field public static final java.lang.String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; + field public static final java.lang.String EXTRA_UUID = "android.os.storage.extra.UUID"; field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 + field public static final java.util.UUID UUID_DEFAULT; } public final class StorageVolume implements android.os.Parcelable { @@ -38136,7 +38102,6 @@ package android.provider { field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; field public static final java.lang.String MODE_RINGER = "mode_ringer"; field public static final java.lang.String NETWORK_PREFERENCE = "network_preference"; - field public static final java.lang.String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled"; field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth"; field public static final java.lang.String RADIO_CELL = "cell"; @@ -38586,6 +38551,16 @@ package android.provider { field public static final java.lang.String SUBSCRIPTION_ID = "pending_sub_id"; } + public static final class Telephony.ServiceStateTable { + method public static android.net.Uri getUriForSubId(int); + method public static android.net.Uri getUriForSubIdAndField(int, java.lang.String); + field public static final java.lang.String AUTHORITY = "service-state"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection"; + field public static final java.lang.String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; + field public static final java.lang.String VOICE_REG_STATE = "voice_reg_state"; + } + public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { method public static java.lang.String getDefaultSmsPackage(android.content.Context); field public static final android.net.Uri CONTENT_URI; @@ -40172,6 +40147,7 @@ package android.service.autofill { public abstract class AutofillService extends android.app.Service { ctor public AutofillService(); method public final deprecated void disableSelf(); + method public final android.service.autofill.FillEventHistory getFillEventHistory(); method public final android.os.IBinder onBind(android.content.Intent); method public void onConnected(); method public void onDisconnected(); @@ -40194,6 +40170,7 @@ package android.service.autofill { ctor public Dataset.Builder(); method public android.service.autofill.Dataset build(); method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.Dataset.Builder setId(java.lang.String); method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue); method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews); } @@ -40211,6 +40188,23 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR; } + public final class FillEventHistory implements android.os.Parcelable { + method public int describeContents(); + method public android.os.Bundle getClientState(); + method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR; + } + + public static final class FillEventHistory.Event { + method public java.lang.String getDatasetId(); + method public int getType(); + field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2 + field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1 + field public static final int TYPE_DATASET_SELECTED = 0; // 0x0 + field public static final int TYPE_SAVE_SHOWN = 3; // 0x3 + } + public final class FillRequest implements android.os.Parcelable { method public int describeContents(); method public android.os.Bundle getClientState(); @@ -40235,6 +40229,7 @@ package android.service.autofill { method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews); method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle); method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); + method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...); method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo); } @@ -40247,6 +40242,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; + field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 @@ -40259,6 +40255,7 @@ package android.service.autofill { ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]); method public android.service.autofill.SaveInfo build(); method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + method public android.service.autofill.SaveInfo.Builder setFlags(int); method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender); method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]); } @@ -40452,7 +40449,6 @@ package android.service.media { method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>); method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle); method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>); - method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>); method public void setSessionToken(android.media.session.MediaSession.Token); field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; } @@ -40560,16 +40556,16 @@ package android.service.notification { method public final int getCurrentInterruptionFilter(); method public final int getCurrentListenerHints(); method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); - method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String); - method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); + method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String, android.os.UserHandle); + method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String, android.os.UserHandle); method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications(); method public android.os.IBinder onBind(android.content.Intent); method public void onInterruptionFilterChanged(int); method public void onListenerConnected(); method public void onListenerDisconnected(); method public void onListenerHintsChanged(int); - method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int); - method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int); + method public void onNotificationChannelGroupModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannelGroup, int); + method public void onNotificationChannelModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannel, int); method public void onNotificationPosted(android.service.notification.StatusBarNotification); method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); @@ -40586,7 +40582,7 @@ package android.service.notification { method public final void snoozeNotification(java.lang.String, java.lang.String); method public final void snoozeNotification(java.lang.String, long); method public void unregisterAsSystemService() throws android.os.RemoteException; - method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); + method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel); field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4 field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2 @@ -44474,7 +44470,6 @@ package android.test.mock { method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; method public android.content.ComponentName startService(android.content.Intent); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean stopService(android.content.Intent); method public void unbindService(android.content.ServiceConnection); method public void unregisterReceiver(android.content.BroadcastReceiver); @@ -44543,6 +44538,7 @@ package android.test.mock { method public int checkPermission(java.lang.String, java.lang.String); method public int checkSignatures(java.lang.String, java.lang.String); method public int checkSignatures(int, int); + method public void clearInstantAppCookie(); method public void clearPackagePreferredActivities(java.lang.String); method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public void extendVerificationTimeout(int, int, long); @@ -44574,7 +44570,7 @@ package android.test.mock { method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method public java.lang.String getInstallerPackageName(java.lang.String); method public byte[] getInstantAppCookie(); - method public int getInstantAppCookieMaxSize(); + method public int getInstantAppCookieMaxBytes(); method public android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String); method public android.content.ComponentName getInstantAppInstallerComponent(); method public android.content.ComponentName getInstantAppResolverSettingsComponent(); @@ -44639,8 +44635,8 @@ package android.test.mock { method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); - method public boolean setInstantAppCookie(byte[]); method public void setUpdateAvailable(java.lang.String, boolean); + method public void updateInstantAppCookie(byte[]); method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int); method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle); method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>); @@ -49527,7 +49523,9 @@ package android.view { field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0 field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2 + field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8 field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1 + field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 @@ -53675,6 +53673,7 @@ package android.widget { method public java.lang.String getFormat(); method public android.widget.Chronometer.OnChronometerTickListener getOnChronometerTickListener(); method public boolean isCountDown(); + method public boolean isTheFinalCountDown(); method public void setBase(long); method public void setCountDown(boolean); method public void setFormat(java.lang.String); @@ -59344,6 +59343,7 @@ package java.lang.invoke { method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...); method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType); + method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType); method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...); method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); diff --git a/api/system-removed.txt b/api/system-removed.txt index 7bdb9571bad0..559ce12f3421 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -20,6 +20,20 @@ package android.app.admin { } +package android.app.usage { + + public class StorageStatsManager { + method public deprecated long getFreeBytes(java.lang.String) throws java.io.IOException; + method public deprecated long getTotalBytes(java.lang.String) throws java.io.IOException; + method public deprecated boolean isQuotaSupported(java.lang.String); + method public deprecated android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException; + method public deprecated android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method public deprecated android.app.usage.StorageStats queryStatsForUid(java.lang.String, int) throws java.io.IOException; + method public deprecated android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException; + } + +} + package android.content { public abstract class Context { @@ -35,6 +49,10 @@ package android.content { package android.content.pm { + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public deprecated java.lang.String volumeUuid; + } + public class ComponentInfo extends android.content.pm.PackageItemInfo { field public deprecated boolean encryptionAware; } @@ -43,6 +61,16 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 } + public abstract class PackageManager { + method public abstract boolean setInstantAppCookie(byte[]); + } + + public final class SharedLibraryInfo implements android.os.Parcelable { + method public boolean isBuiltin(); + method public boolean isDynamic(); + method public boolean isStatic(); + } + } package android.database { @@ -106,6 +134,16 @@ package android.hardware { } +package android.location { + + public class Location implements android.os.Parcelable { + method public deprecated void removeBearingAccuracy(); + method public deprecated void removeSpeedAccuracy(); + method public deprecated void removeVerticalAccuracy(); + } + +} + package android.media { public final class AudioFormat implements android.os.Parcelable { @@ -174,10 +212,14 @@ package android.os { package android.os.storage { public class StorageManager { - method public deprecated long getCacheQuotaBytes(); - method public deprecated long getCacheSizeBytes(); - method public deprecated long getExternalCacheQuotaBytes(); - method public deprecated long getExternalCacheSizeBytes(); + method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException; + method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException; + method public deprecated long getCacheQuotaBytes() throws java.io.IOException; + method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException; + method public deprecated long getCacheSizeBytes() throws java.io.IOException; + method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException; + method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException; method public android.os.storage.StorageVolume getPrimaryVolume(); method public android.os.storage.StorageVolume[] getVolumeList(); method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException; diff --git a/api/test-current.txt b/api/test-current.txt index bd2fed09c9d6..6c1231b93b24 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1710,6 +1710,7 @@ package android { field public static final int ic_notification_clear_all = 17301594; // 0x108005a field public static final int ic_notification_overlay = 17301595; // 0x108005b field public static final int ic_partial_secure = 17301596; // 0x108005c + field public static final int ic_picture_in_picture = 17301685; // 0x10800b5 field public static final int ic_popup_disk_full = 17301597; // 0x108005d field public static final int ic_popup_reminder = 17301598; // 0x108005e field public static final int ic_popup_sync = 17301599; // 0x108005f @@ -3852,7 +3853,7 @@ package android.app { method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException; method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses(); method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException; - method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; + method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException; method public int getUidImportance(int); method public deprecated boolean isInLockTaskMode(); @@ -4590,6 +4591,7 @@ package android.app { method public final android.app.FragmentManager getFragmentManager(); method public final java.lang.Object getHost(); method public final int getId(); + method public final android.view.LayoutInflater getLayoutInflater(); method public android.app.LoaderManager getLoaderManager(); method public final android.app.Fragment getParentFragment(); method public android.transition.Transition getReenterTransition(); @@ -5618,7 +5620,6 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -6655,7 +6656,7 @@ package android.app.assist { public static class AssistStructure.ViewNode { method public float getAlpha(); - method public java.lang.String[] getAutoFillHints(); + method public java.lang.String[] getAutofillHints(); method public android.view.autofill.AutofillId getAutofillId(); method public java.lang.String[] getAutofillOptions(); method public int getAutofillType(); @@ -6920,7 +6921,7 @@ package android.app.job { } public abstract class JobServiceEngine { - ctor public JobServiceEngine(android.content.Context); + ctor public JobServiceEngine(android.app.Service); method public final android.os.IBinder getBinder(); method public final void jobFinished(android.app.job.JobParameters, boolean); method public abstract boolean onStartJob(android.app.job.JobParameters); @@ -7024,13 +7025,13 @@ package android.app.usage { } public class StorageStatsManager { - method public long getFreeBytes(java.lang.String); - method public long getTotalBytes(java.lang.String); - method public boolean isQuotaSupported(java.lang.String); - method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle); - method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle); - method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int); - method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle); + method public long getFreeBytes(java.util.UUID) throws java.io.IOException; + method public long getTotalBytes(java.util.UUID) throws java.io.IOException; + method public boolean isQuotaSupported(java.util.UUID); + method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException; + method public android.app.usage.StorageStats queryStatsForPackage(java.util.UUID, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method public android.app.usage.StorageStats queryStatsForUid(java.util.UUID, int) throws java.io.IOException; + method public android.app.usage.StorageStats queryStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException; } public final class UsageEvents implements android.os.Parcelable { @@ -7095,6 +7096,7 @@ package android.appwidget { method public static void deleteAllHosts(); method public void deleteAppWidgetId(int); method public void deleteHost(); + method public int[] getAppWidgetIds(); method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo); method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo); method protected void onProvidersChanged(); @@ -9416,7 +9418,6 @@ package android.content { field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; - field public static final java.lang.String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED"; field public static final java.lang.String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; field public static final java.lang.String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED"; field public static final deprecated java.lang.String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; @@ -9926,7 +9927,7 @@ package android.content { } public abstract interface ServiceConnection { - method public default void onBindingDead(android.content.ComponentName); + method public default void onBindingDied(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } @@ -10253,12 +10254,12 @@ package android.content.pm { field public java.lang.String[] splitNames; field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; + field public java.util.UUID storageUuid; field public int targetSdkVersion; field public java.lang.String taskAffinity; field public int theme; field public int uiOptions; field public int uid; - field public java.lang.String volumeUuid; } public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator { @@ -10378,7 +10379,7 @@ package android.content.pm { public class LauncherApps { ctor public LauncherApps(android.content.Context); method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle); - method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle); + method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent); method public java.util.List<android.os.UserHandle> getProfiles(); method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); @@ -10607,6 +10608,7 @@ package android.content.pm { method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); method public abstract int checkSignatures(int, int); + method public abstract void clearInstantAppCookie(); method public abstract void clearPackagePreferredActivities(java.lang.String); method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public abstract void extendVerificationTimeout(int, int, long); @@ -10637,7 +10639,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public abstract java.lang.String getInstallerPackageName(java.lang.String); method public abstract byte[] getInstantAppCookie(); - method public abstract int getInstantAppCookieMaxSize(); + method public abstract int getInstantAppCookieMaxBytes(); method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String); method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); @@ -10693,7 +10695,7 @@ package android.content.pm { method public abstract void setApplicationEnabledSetting(java.lang.String, int, int); method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); - method public abstract boolean setInstantAppCookie(byte[]); + method public abstract void updateInstantAppCookie(byte[]); method public abstract void verifyPendingInstall(int, int); field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0 field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2 @@ -10982,12 +10984,13 @@ package android.content.pm { method public android.content.pm.VersionedPackage getDeclaringPackage(); method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); method public java.lang.String getName(); - method public int getVersion(); - method public boolean isBuiltin(); - method public boolean isDynamic(); - method public boolean isStatic(); + method public int getType(); + method public long getVersion(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR; + field public static final int TYPE_BUILTIN = 0; // 0x0 + field public static final int TYPE_DYNAMIC = 1; // 0x1 + field public static final int TYPE_STATIC = 2; // 0x2 field public static final int VERSION_UNDEFINED = -1; // 0xffffffff } @@ -12642,7 +12645,6 @@ package android.graphics { public class BitmapShader extends android.graphics.Shader { ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); - method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); } public class BlurMaskFilter extends android.graphics.MaskFilter { @@ -12895,8 +12897,6 @@ package android.graphics { ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix); ctor public ColorMatrixColorFilter(float[]); method public void getColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrix(android.graphics.ColorMatrix); - method public void setColorMatrixArray(float[]); } public abstract class ColorSpace { @@ -13037,8 +13037,6 @@ package android.graphics { public class ComposeShader extends android.graphics.Shader { ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode); - method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -13111,15 +13109,11 @@ package android.graphics { ctor public LightingColorFilter(int, int); method public int getColorAdd(); method public int getColorMultiply(); - method public void setColorAdd(int); - method public void setColorMultiply(int); } public class LinearGradient extends android.graphics.Shader { ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode); } public class MaskFilter { @@ -13629,10 +13623,6 @@ package android.graphics { public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode); - method public int getColor(); - method public android.graphics.PorterDuff.Mode getMode(); - method public void setColor(int); - method public void setMode(android.graphics.PorterDuff.Mode); } public class PorterDuffXfermode extends android.graphics.Xfermode { @@ -13642,8 +13632,6 @@ package android.graphics { public class RadialGradient extends android.graphics.Shader { ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode); ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); - method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode); - method public void set(float, float, float, int, int, android.graphics.Shader.TileMode); } public final class Rect implements android.os.Parcelable { @@ -13828,8 +13816,6 @@ package android.graphics { public class SweepGradient extends android.graphics.Shader { ctor public SweepGradient(float, float, int[], float[]); ctor public SweepGradient(float, float, int, int); - method public void set(float, float, int[], float[]); - method public void set(float, float, int, int); } public class Typeface { @@ -14878,7 +14864,7 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light"; field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration"; - field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody"; + field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody_detect"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated"; field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; @@ -20907,13 +20893,10 @@ package android.location { method public boolean hasSpeedAccuracy(); method public boolean hasVerticalAccuracy(); method public boolean isFromMockProvider(); - method public void removeAccuracy(); - method public void removeAltitude(); - method public void removeBearing(); - method public void removeBearingAccuracy(); - method public void removeSpeed(); - method public void removeSpeedAccuracy(); - method public void removeVerticalAccuracy(); + method public deprecated void removeAccuracy(); + method public deprecated void removeAltitude(); + method public deprecated void removeBearing(); + method public deprecated void removeSpeed(); method public void reset(); method public void set(android.location.Location); method public void setAccuracy(float); @@ -22882,7 +22865,7 @@ package android.media { method public android.media.MediaPlayer.DrmInfo getDrmInfo(); method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException; method public int getDuration(); - method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; + method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException; method public android.os.PersistableBundle getMetrics(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -22896,7 +22879,7 @@ package android.media { method public void pause() throws java.lang.IllegalStateException; method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void prepareAsync() throws java.lang.IllegalStateException; - method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; + method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException; method public void release(); method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException; @@ -22923,7 +22906,7 @@ package android.media { method public void setNextMediaPlayer(android.media.MediaPlayer); method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener); method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener); - method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener); + method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener); method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler); method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener); @@ -22964,6 +22947,10 @@ package android.media { field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3 field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1 + field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2 + field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0 field public static final int SEEK_CLOSEST = 3; // 0x3 field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2 field public static final int SEEK_NEXT_SYNC = 1; // 0x1 @@ -22973,7 +22960,6 @@ package android.media { } public static final class MediaPlayer.DrmInfo { - method public java.lang.String[] getMimes(); method public java.util.Map<java.util.UUID, byte[]> getPssh(); method public java.util.UUID[] getSupportedSchemes(); } @@ -23005,7 +22991,7 @@ package android.media { method public abstract void onCompletion(android.media.MediaPlayer); } - public static abstract interface MediaPlayer.OnDrmConfigListener { + public static abstract interface MediaPlayer.OnDrmConfigHelper { method public abstract void onDrmConfig(android.media.MediaPlayer); } @@ -23014,7 +23000,7 @@ package android.media { } public static abstract interface MediaPlayer.OnDrmPreparedListener { - method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean); + method public abstract void onDrmPrepared(android.media.MediaPlayer, int); } public static abstract interface MediaPlayer.OnErrorListener { @@ -23045,8 +23031,12 @@ package android.media { method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int); } - public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException { - ctor public MediaPlayer.ProvisioningErrorException(java.lang.String); + public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String); + } + + public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException { + ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String); } public static class MediaPlayer.TrackInfo implements android.os.Parcelable { @@ -24118,7 +24108,6 @@ package android.media.browse { method public android.content.ComponentName getServiceComponent(); method public android.media.session.MediaSession.Token getSessionToken(); method public boolean isConnected(); - method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback); method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback); method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback); method public void unsubscribe(java.lang.String); @@ -24154,12 +24143,6 @@ package android.media.browse { field public static final int FLAG_PLAYABLE = 2; // 0x2 } - public static abstract class MediaBrowser.SearchCallback { - ctor public MediaBrowser.SearchCallback(); - method public void onError(java.lang.String, android.os.Bundle); - method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>); - } - public static abstract class MediaBrowser.SubscriptionCallback { ctor public MediaBrowser.SubscriptionCallback(); method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>); @@ -24368,8 +24351,6 @@ package android.media.session { public final class MediaController { ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token); - method public void addQueueItem(android.media.MediaDescription); - method public void addQueueItem(android.media.MediaDescription, int); method public void adjustVolume(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public android.os.Bundle getExtras(); @@ -24381,15 +24362,11 @@ package android.media.session { method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue(); method public java.lang.CharSequence getQueueTitle(); method public int getRatingType(); - method public int getRepeatMode(); method public android.app.PendingIntent getSessionActivity(); method public android.media.session.MediaSession.Token getSessionToken(); method public android.media.session.MediaController.TransportControls getTransportControls(); - method public boolean isShuffleModeEnabled(); method public void registerCallback(android.media.session.MediaController.Callback); method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler); - method public void removeQueueItem(android.media.MediaDescription); - method public void removeQueueItemAt(int); method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void setVolumeTo(int, int); method public void unregisterCallback(android.media.session.MediaController.Callback); @@ -24403,10 +24380,8 @@ package android.media.session { method public void onPlaybackStateChanged(android.media.session.PlaybackState); method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>); method public void onQueueTitleChanged(java.lang.CharSequence); - method public void onRepeatModeChanged(int); method public void onSessionDestroyed(); method public void onSessionEvent(java.lang.String, android.os.Bundle); - method public void onShuffleModeChanged(boolean); } public static final class MediaController.PlaybackInfo { @@ -24435,8 +24410,6 @@ package android.media.session { method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle); method public void sendCustomAction(java.lang.String, android.os.Bundle); method public void setRating(android.media.Rating); - method public void setRepeatMode(int); - method public void setShuffleModeEnabled(boolean); method public void skipToNext(); method public void skipToPrevious(); method public void skipToQueueItem(long); @@ -24463,18 +24436,13 @@ package android.media.session { method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>); method public void setQueueTitle(java.lang.CharSequence); method public void setRatingType(int); - method public void setRepeatMode(int); method public void setSessionActivity(android.app.PendingIntent); - method public void setShuffleModeEnabled(boolean); field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 - field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4 field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class MediaSession.Callback { ctor public MediaSession.Callback(); - method public void onAddQueueItem(android.media.MediaDescription); - method public void onAddQueueItem(android.media.MediaDescription, int); method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void onCustomAction(java.lang.String, android.os.Bundle); method public void onFastForward(); @@ -24488,13 +24456,9 @@ package android.media.session { method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle); method public void onPrepareFromSearch(java.lang.String, android.os.Bundle); method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); - method public void onRemoveQueueItem(android.media.MediaDescription); - method public void onRemoveQueueItemAt(int); method public void onRewind(); method public void onSeekTo(long); method public void onSetRating(android.media.Rating); - method public void onSetRepeatMode(int); - method public void onSetShuffleModeEnabled(boolean); method public void onSkipToNext(); method public void onSkipToPrevious(); method public void onSkipToQueueItem(long); @@ -24555,17 +24519,12 @@ package android.media.session { field public static final long ACTION_REWIND = 8L; // 0x8L field public static final long ACTION_SEEK_TO = 256L; // 0x100L field public static final long ACTION_SET_RATING = 128L; // 0x80L - field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L - field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L field public static final long ACTION_STOP = 1L; // 0x1L field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR; field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL - field public static final int REPEAT_MODE_ALL = 2; // 0x2 - field public static final int REPEAT_MODE_NONE = 0; // 0x0 - field public static final int REPEAT_MODE_ONE = 1; // 0x1 field public static final int STATE_BUFFERING = 6; // 0x6 field public static final int STATE_CONNECTING = 8; // 0x8 field public static final int STATE_ERROR = 7; // 0x7 @@ -25666,14 +25625,10 @@ package android.net { } public final class IpSecManager { - method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException; method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform); - method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform); - method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform); + method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 @@ -25692,7 +25647,7 @@ package android.net { } public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public void close(); + method public void close() throws java.io.IOException; method public int getPort(); method public java.io.FileDescriptor getSocket(); } @@ -26890,8 +26845,8 @@ package android.net.wifi.aware { } public class DiscoverySession { - method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); - method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String); method public void destroy(); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -26977,8 +26932,8 @@ package android.net.wifi.aware { } public class WifiAwareSession { - method public java.lang.String createNetworkSpecifierOpen(int, byte[]); - method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String); + method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]); + method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String); method public void destroy(); method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler); @@ -30909,6 +30864,7 @@ package android.os { method public android.util.SizeF getSizeF(java.lang.String); method public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(java.lang.String); method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String); + method public java.util.UUID getUuid(java.lang.String); method public boolean hasFileDescriptors(); method public void putAll(android.os.Bundle); method public void putBinder(java.lang.String, android.os.IBinder); @@ -30933,6 +30889,7 @@ package android.os { method public void putSizeF(java.lang.String, android.util.SizeF); method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>); method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>); + method public void putUuid(java.lang.String, java.util.UUID); method public void readFromParcel(android.os.Parcel); method public void setClassLoader(java.lang.ClassLoader); method public void writeToParcel(android.os.Parcel, int); @@ -31483,6 +31440,7 @@ package android.os { method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>); method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>); method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>); + method public final java.util.UUID readUuid(); method public final java.lang.Object readValue(java.lang.ClassLoader); method public final void recycle(); method public final void setDataCapacity(int); @@ -31528,6 +31486,7 @@ package android.os { method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int); method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>); method public final <T extends android.os.Parcelable> void writeTypedObject(T, int); + method public final void writeUuid(java.util.UUID); method public final void writeValue(java.lang.Object); field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR; } @@ -32162,15 +32121,16 @@ package android.os.storage { } public class StorageManager { - method public void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException; method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException; - method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException; - method public long getCacheQuotaBytes(java.io.File); - method public long getCacheSizeBytes(java.io.File); + method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException; + method public long getCacheQuotaBytes(java.util.UUID) throws java.io.IOException; + method public long getCacheSizeBytes(java.util.UUID) throws java.io.IOException; method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryStorageVolume(); method public android.os.storage.StorageVolume getStorageVolume(java.io.File); method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); + method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException; method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException; method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException; method public boolean isEncrypted(java.io.File); @@ -32182,7 +32142,10 @@ package android.os.storage { method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + field public static final java.lang.String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; + field public static final java.lang.String EXTRA_UUID = "android.os.storage.extra.UUID"; field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 + field public static final java.util.UUID UUID_DEFAULT; } public final class StorageVolume implements android.os.Parcelable { @@ -35627,6 +35590,16 @@ package android.provider { field public static final java.lang.String SUBSCRIPTION_ID = "pending_sub_id"; } + public static final class Telephony.ServiceStateTable { + method public static android.net.Uri getUriForSubId(int); + method public static android.net.Uri getUriForSubIdAndField(int, java.lang.String); + field public static final java.lang.String AUTHORITY = "service-state"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection"; + field public static final java.lang.String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; + field public static final java.lang.String VOICE_REG_STATE = "voice_reg_state"; + } + public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { method public static java.lang.String getDefaultSmsPackage(android.content.Context); field public static final android.net.Uri CONTENT_URI; @@ -37214,6 +37187,7 @@ package android.service.autofill { public abstract class AutofillService extends android.app.Service { ctor public AutofillService(); method public final deprecated void disableSelf(); + method public final android.service.autofill.FillEventHistory getFillEventHistory(); method public final android.os.IBinder onBind(android.content.Intent); method public void onConnected(); method public void onDisconnected(); @@ -37236,6 +37210,7 @@ package android.service.autofill { ctor public Dataset.Builder(); method public android.service.autofill.Dataset build(); method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.Dataset.Builder setId(java.lang.String); method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue); method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews); } @@ -37253,6 +37228,23 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR; } + public final class FillEventHistory implements android.os.Parcelable { + method public int describeContents(); + method public android.os.Bundle getClientState(); + method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR; + } + + public static final class FillEventHistory.Event { + method public java.lang.String getDatasetId(); + method public int getType(); + field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2 + field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1 + field public static final int TYPE_DATASET_SELECTED = 0; // 0x0 + field public static final int TYPE_SAVE_SHOWN = 3; // 0x3 + } + public final class FillRequest implements android.os.Parcelable { method public int describeContents(); method public android.os.Bundle getClientState(); @@ -37277,6 +37269,7 @@ package android.service.autofill { method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews); method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle); method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); + method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...); method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo); } @@ -37289,6 +37282,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR; + field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 @@ -37301,6 +37295,7 @@ package android.service.autofill { ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]); method public android.service.autofill.SaveInfo build(); method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + method public android.service.autofill.SaveInfo.Builder setFlags(int); method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender); method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]); } @@ -37494,7 +37489,6 @@ package android.service.media { method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>); method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle); method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>); - method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>); method public void setSessionToken(android.media.session.MediaSession.Token); field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; } @@ -37600,16 +37594,16 @@ package android.service.notification { method public final int getCurrentInterruptionFilter(); method public final int getCurrentListenerHints(); method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); - method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String); - method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); + method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String, android.os.UserHandle); + method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String, android.os.UserHandle); method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications(); method public android.os.IBinder onBind(android.content.Intent); method public void onInterruptionFilterChanged(int); method public void onListenerConnected(); method public void onListenerDisconnected(); method public void onListenerHintsChanged(int); - method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int); - method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int); + method public void onNotificationChannelGroupModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannelGroup, int); + method public void onNotificationChannelModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannel, int); method public void onNotificationPosted(android.service.notification.StatusBarNotification); method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); @@ -37623,7 +37617,7 @@ package android.service.notification { method public final void setNotificationsShown(java.lang.String[]); method public final void snoozeNotification(java.lang.String, java.lang.String); method public final void snoozeNotification(java.lang.String, long); - method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); + method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel); field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4 field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2 @@ -41114,7 +41108,6 @@ package android.test.mock { method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; method public android.content.ComponentName startService(android.content.Intent); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean stopService(android.content.Intent); method public void unbindService(android.content.ServiceConnection); method public void unregisterReceiver(android.content.BroadcastReceiver); @@ -41182,6 +41175,7 @@ package android.test.mock { method public int checkPermission(java.lang.String, java.lang.String); method public int checkSignatures(java.lang.String, java.lang.String); method public int checkSignatures(int, int); + method public void clearInstantAppCookie(); method public void clearPackagePreferredActivities(java.lang.String); method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]); method public void extendVerificationTimeout(int, int, long); @@ -41213,7 +41207,7 @@ package android.test.mock { method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public java.lang.String getInstallerPackageName(java.lang.String); method public byte[] getInstantAppCookie(); - method public int getInstantAppCookieMaxSize(); + method public int getInstantAppCookieMaxBytes(); method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.Intent getLaunchIntentForPackage(java.lang.String); method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); @@ -41268,7 +41262,7 @@ package android.test.mock { method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); - method public boolean setInstantAppCookie(byte[]); + method public void updateInstantAppCookie(byte[]); method public void verifyPendingInstall(int, int); } @@ -46323,7 +46317,9 @@ package android.view { field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1 field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0 field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2 + field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8 field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1 + field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4 field public static final int INVISIBLE = 4; // 0x4 field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000 field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2 @@ -50112,6 +50108,7 @@ package android.widget { method public java.lang.String getFormat(); method public android.widget.Chronometer.OnChronometerTickListener getOnChronometerTickListener(); method public boolean isCountDown(); + method public boolean isTheFinalCountDown(); method public void setBase(long); method public void setCountDown(boolean); method public void setFormat(java.lang.String); @@ -55795,6 +55792,7 @@ package java.lang.invoke { method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...); method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType); + method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType); method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...); method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); diff --git a/api/test-removed.txt b/api/test-removed.txt index 1e8370e97dc5..189536d8c695 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -22,6 +22,20 @@ package android.app.admin { } +package android.app.usage { + + public class StorageStatsManager { + method public deprecated long getFreeBytes(java.lang.String) throws java.io.IOException; + method public deprecated long getTotalBytes(java.lang.String) throws java.io.IOException; + method public deprecated boolean isQuotaSupported(java.lang.String); + method public deprecated android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException; + method public deprecated android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method public deprecated android.app.usage.StorageStats queryStatsForUid(java.lang.String, int) throws java.io.IOException; + method public deprecated android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException; + } + +} + package android.content { public abstract class Context { @@ -37,6 +51,10 @@ package android.content { package android.content.pm { + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public deprecated java.lang.String volumeUuid; + } + public class ComponentInfo extends android.content.pm.PackageItemInfo { field public deprecated boolean encryptionAware; } @@ -45,6 +63,16 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 } + public abstract class PackageManager { + method public abstract boolean setInstantAppCookie(byte[]); + } + + public final class SharedLibraryInfo implements android.os.Parcelable { + method public boolean isBuiltin(); + method public boolean isDynamic(); + method public boolean isStatic(); + } + } package android.database { @@ -108,6 +136,16 @@ package android.hardware { } +package android.location { + + public class Location implements android.os.Parcelable { + method public deprecated void removeBearingAccuracy(); + method public deprecated void removeSpeedAccuracy(); + method public deprecated void removeVerticalAccuracy(); + } + +} + package android.media { public final class AudioFormat implements android.os.Parcelable { @@ -180,10 +218,14 @@ package android.os { package android.os.storage { public class StorageManager { - method public deprecated long getCacheQuotaBytes(); - method public deprecated long getCacheSizeBytes(); - method public deprecated long getExternalCacheQuotaBytes(); - method public deprecated long getExternalCacheSizeBytes(); + method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException; + method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException; + method public deprecated long getCacheQuotaBytes() throws java.io.IOException; + method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException; + method public deprecated long getCacheSizeBytes() throws java.io.IOException; + method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException; + method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException; method public android.os.storage.StorageVolume getPrimaryVolume(); method public android.os.storage.StorageVolume[] getVolumeList(); method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException; diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp index 67874a8b9c02..d69dd79555a1 100644 --- a/cmds/idmap/scan.cpp +++ b/cmds/idmap/scan.cpp @@ -10,6 +10,7 @@ #include <androidfw/StreamingZipInflater.h> #include <androidfw/ZipFileRO.h> #include <cutils/jstring.h> +#include <cutils/properties.h> #include <private/android_filesystem_config.h> // for AID_SYSTEM #include <utils/SortedVector.h> #include <utils/String16.h> @@ -82,12 +83,26 @@ namespace { return String8(tmp); } + bool check_property(String16 property, String16 value) { + const char *prop; + const char *val; + + prop = strndup16to8(property.string(), property.size()); + char propBuf[PROPERTY_VALUE_MAX]; + property_get(prop, propBuf, NULL); + val = strndup16to8(value.string(), value.size()); + + return (strcmp(propBuf, val) == 0); + } + int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name, bool* is_static_overlay) { const size_t N = parser.getAttributeCount(); String16 target; int priority = -1; + String16 propName = String16(); + String16 propValue = String16(); for (size_t i = 0; i < N; ++i) { size_t len; String16 key(parser.getAttributeName(i, &len)); @@ -109,34 +124,32 @@ namespace { if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) { *is_static_overlay = (v.data != 0); } - } - } - if (target == String16(target_package_name)) { - return priority; - } - return NO_OVERLAY_TAG; - } - - String16 parse_package_name(const ResXMLTree& parser) - { - const size_t N = parser.getAttributeCount(); - String16 package_name; - for (size_t i = 0; i < N; ++i) { - size_t len; - String16 key(parser.getAttributeName(i, &len)); - if (key == String16("package")) { + } else if (key == String16("requiredSystemPropertyName")) { const char16_t *p = parser.getAttributeStringValue(i, &len); if (p != NULL) { - package_name = String16(p, len); + propName = String16(p, len); + } + } else if (key == String16("requiredSystemPropertyValue")) { + const char16_t *p = parser.getAttributeStringValue(i, &len); + if (p != NULL) { + propValue = String16(p, len); } } } - return package_name; - } - bool isValidStaticOverlayPackage(const String16& package_name) { - // TODO(b/35742444): Need to support selection method based on a package name. - return package_name.size() > 0; + // Note that conditional property enablement/exclusion only applies if + // the attribute is present. In its absence, all overlays are presumed enabled. + if (propName.size() > 0 && propValue.size() > 0) { + // if property set & equal to value, then include overlay - otherwise skip + if (!check_property(propName, propValue)) { + return NO_OVERLAY_TAG; + } + } + + if (target == String16(target_package_name)) { + return priority; + } + return NO_OVERLAY_TAG; } int parse_manifest(const void *data, size_t size, const char *target_package_name) @@ -149,7 +162,6 @@ namespace { } ResXMLParser::event_code_t type; - String16 package_name; bool is_static_overlay = false; int priority = NO_OVERLAY_TAG; do { @@ -157,16 +169,14 @@ namespace { if (type == ResXMLParser::START_TAG) { size_t len; String16 tag(parser.getElementName(&len)); - if (tag == String16("manifest")) { - package_name = parse_package_name(parser); - } else if (tag == String16("overlay")) { + if (tag == String16("overlay")) { priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay); break; } } } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT); - if (is_static_overlay && isValidStaticOverlayPackage(package_name)) { + if (is_static_overlay) { return priority; } return NO_OVERLAY_TAG; diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index 4be46540e19b..9df229cbf490 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -228,16 +228,6 @@ public class Media extends BaseCommand { System.out.println("onVolumeInfoChanged " + info); } - @Override - public void onRepeatModeChanged(int repeatMode) throws RemoteException { - System.out.println("onRepeatModeChanged " + repeatMode); - } - - @Override - public void onShuffleModeChanged(boolean enabled) throws RemoteException { - System.out.println("onShuffleModeChanged " + enabled); - } - void printUsageMessage() { try { System.out.println("V2Monitoring session " + mController.getTag() diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index b36a1600bae5..c4b7ed771b70 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,21 +16,16 @@ package android.app; -import android.metrics.LogMaker; import android.graphics.Rect; import android.os.SystemClock; import android.view.ViewRootImpl.ActivityConfigCallback; -import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillPopupWindow; -import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ToolbarActionBar; import com.android.internal.app.WindowDecorActionBar; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; import com.android.internal.policy.PhoneWindow; import android.annotation.CallSuper; @@ -1234,6 +1229,13 @@ public class Activity extends ContextThemeWrapper mFragments.doLoaderStart(); getApplication().dispatchActivityStarted(this); + + if (mAutoFillResetNeeded) { + AutofillManager afm = getAutofillManager(); + if (afm != null) { + afm.onVisibleForAutofill(); + } + } } /** @@ -2068,6 +2070,7 @@ public class Activity extends ContextThemeWrapper if (args == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture args"); } + updatePictureInPictureArgsForContentInsets(args); return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, args); } catch (RemoteException e) { return false; @@ -2085,11 +2088,27 @@ public class Activity extends ContextThemeWrapper if (args == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture args"); } + updatePictureInPictureArgsForContentInsets(args); ActivityManagerNative.getDefault().setPictureInPictureArgs(mToken, args); } catch (RemoteException e) { } } + /** + * Updates the provided {@param args} with the last known content insets for this activity, to + * be used with the source hint rect for the transition into PiP. + */ + private void updatePictureInPictureArgsForContentInsets(PictureInPictureArgs args) { + if (args != null && args.hasSourceBoundsHint() && getWindow() != null && + getWindow().peekDecorView() != null && + getWindow().peekDecorView().getViewRootImpl() != null) { + args.setSourceRectHintInsets( + getWindow().peekDecorView().getViewRootImpl().getLastContentInsets()); + } else { + args.setSourceRectHintInsets(null); + } + } + void dispatchMovedToDisplay(int displayId, Configuration config) { updateDisplay(displayId); onMovedToDisplay(displayId, config); @@ -7407,6 +7426,54 @@ public class Activity extends ContextThemeWrapper return true; } + /** @hide */ + @Override + public boolean getViewVisibility(int viewId) { + Window window = getWindow(); + if (window == null) { + Log.i(TAG, "no window"); + return false; + } + + View decorView = window.peekDecorView(); + if (decorView == null) { + Log.i(TAG, "no decorView"); + return false; + } + + View view = decorView.findViewByAccessibilityIdTraversal(viewId); + if (view == null) { + Log.i(TAG, "cannot find view"); + return false; + } + + // Check if the view is visible by checking all parents + while (view != null) { + if (view == decorView) { + break; + } + + if (view.getVisibility() != View.VISIBLE) { + Log.i(TAG, view + " is not visible"); + return false; + } + + if (view.getParent() instanceof View) { + view = (View) view.getParent(); + } else { + break; + } + } + + return true; + } + + /** @hide */ + @Override + public boolean isVisibleForAutofill() { + return !mStopped; + } + /** * If set to true, this indicates to the system that it should never take a * screenshot of the activity to be used as a representation while it is not in a started state. diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 66167a3c986a..a4dcf630bc3e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2564,6 +2564,10 @@ public class ActivityManager { * <p><b>Note: this method is only intended for debugging or implementing * service management type user interfaces.</b></p> * + * @deprecated As of {@link android.os.Build.VERSION_CODES#O}, this method + * is no longer available to third party applications. For backwards compatibility, + * it will still return the caller's own services. + * * @param maxNum The maximum number of entries to return in the list. The * actual number returned may be smaller, depending on how many services * are running. @@ -2571,6 +2575,7 @@ public class ActivityManager { * @return Returns a list of RunningServiceInfo records describing each of * the running tasks. */ + @Deprecated public List<RunningServiceInfo> getRunningServices(int maxNum) throws SecurityException { try { diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 02d18841f489..63e8cc641ce9 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1211,9 +1211,6 @@ public class ActivityOptions { * methods that take an options Bundle. */ public Bundle toBundle() { - if (mAnimationType == ANIM_DEFAULT) { - return null; - } Bundle b = new Bundle(); if (mPackageName != null) { b.putString(KEY_PACKAGE_NAME, mPackageName); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index e07b7e4a03a1..e50c307df854 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -79,6 +79,8 @@ import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.provider.Settings; import android.util.ArrayMap; +import android.util.IconDrawableFactory; +import android.util.LauncherIcons; import android.util.Log; import android.view.Display; @@ -818,14 +820,18 @@ public class ApplicationPackageManager extends PackageManager { } } - @Override - public int getInstantAppCookieMaxSize() { + public int getInstantAppCookieMaxBytes() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES, DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES); } @Override + public int getInstantAppCookieMaxSize() { + return getInstantAppCookieMaxBytes(); + } + + @Override public @NonNull byte[] getInstantAppCookie() { try { final byte[] cookie = mPM.getInstantAppCookie( @@ -841,6 +847,25 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public void clearInstantAppCookie() { + updateInstantAppCookie(null); + } + + @Override + public void updateInstantAppCookie(@NonNull byte[] cookie) { + if (cookie != null && cookie.length > getInstantAppCookieMaxBytes()) { + throw new IllegalArgumentException("instant cookie longer than " + + getInstantAppCookieMaxBytes()); + } + try { + mPM.setInstantAppCookie(mContext.getPackageName(), + cookie, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override public boolean setInstantAppCookie(@NonNull byte[] cookie) { try { return mPM.setInstantAppCookie(mContext.getPackageName(), @@ -1245,16 +1270,9 @@ public class ApplicationPackageManager extends PackageManager { if (!isManagedProfile(user.getIdentifier())) { return icon; } - Drawable badgeShadow = getDrawable("system", - com.android.internal.R.drawable.ic_corp_icon_badge_shadow, null); - Drawable badgeColor = getDrawable("system", - com.android.internal.R.drawable.ic_corp_icon_badge_color, null); - badgeColor.setTint(getUserBadgeColor(user)); - Drawable badgeForeground = getDrawable("system", - com.android.internal.R.drawable.ic_corp_icon_badge_case, null); - - Drawable badge = new LayerDrawable( - new Drawable[] {badgeShadow, badgeColor, badgeForeground }); + Drawable badge = new LauncherIcons(mContext).getBadgeDrawable( + com.android.internal.R.drawable.ic_corp_icon_badge_case, + getUserBadgeColor(user)); return getBadgedDrawable(icon, badge, null, true); } @@ -1268,14 +1286,6 @@ public class ApplicationPackageManager extends PackageManager { return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true); } - // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles() - @VisibleForTesting - public static final int[] CORP_BADGE_COLORS = new int[] { - com.android.internal.R.color.profile_badge_1, - com.android.internal.R.color.profile_badge_2, - com.android.internal.R.color.profile_badge_3 - }; - @VisibleForTesting public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] { com.android.internal.R.string.managed_profile_label_badge, @@ -1284,12 +1294,7 @@ public class ApplicationPackageManager extends PackageManager { }; private int getUserBadgeColor(UserHandle user) { - int badge = getUserManager().getManagedProfileBadge(user.getIdentifier()); - if (badge < 0) { - badge = 0; - } - int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length]; - return Resources.getSystem().getColor(resourceId, null); + return IconDrawableFactory.getUserBadgeColor(getUserManager(), user.getIdentifier()); } @Override @@ -2648,4 +2653,13 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowAsRuntimeException(); } } + + @Override + public String getInstantAppAndroidId(String packageName, UserHandle user) { + try { + return mPM.getInstantAppAndroidId(packageName, user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 6cc8a14ea2f9..80de64b94519 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1447,21 +1447,13 @@ class ContextImpl extends Context { @Override public ComponentName startService(Intent service) { warnIfCallingFromSystemProcess(); - return startServiceCommon(service, -1, null, false, mUser); + return startServiceCommon(service, false, mUser); } @Override public ComponentName startForegroundService(Intent service) { warnIfCallingFromSystemProcess(); - return startServiceCommon(service, -1, null, true, mUser); - } - - // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired - @Override - public ComponentName startServiceInForeground(Intent service, - int id, Notification notification) { - warnIfCallingFromSystemProcess(); - return startServiceCommon(service, id, notification, false, mUser); + return startServiceCommon(service, true, mUser); } @Override @@ -1472,29 +1464,22 @@ class ContextImpl extends Context { @Override public ComponentName startServiceAsUser(Intent service, UserHandle user) { - return startServiceCommon(service, -1, null, false, user); + return startServiceCommon(service, false, user); } @Override public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { - return startServiceCommon(service, -1, null, true, user); + return startServiceCommon(service, true, user); } - // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired - @Override - public ComponentName startServiceInForegroundAsUser(Intent service, - int id, Notification notification, UserHandle user) { - return startServiceCommon(service, id, notification, false, user); - } - - private ComponentName startServiceCommon(Intent service, int id, Notification notification, - boolean requireForeground, UserHandle user) { + private ComponentName startServiceCommon(Intent service, boolean requireForeground, + UserHandle user) { try { validateServiceIntent(service); service.prepareToLeaveProcess(this); ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( - getContentResolver()), id, notification, requireForeground, + getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier()); if (cn != null) { if (cn.getPackageName().equals("!")) { diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index c7bcc54b51b6..c5b93cd62b4c 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -509,6 +509,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // True if mHidden has been changed and the animation should be scheduled. boolean mHiddenChanged; + // The cached value from onGetLayoutInflater(Bundle) that will be returned from + // getLayoutInflater() + LayoutInflater mLayoutInflater; + /** * State information that has been retrieved from a fragment instance * through {@link FragmentManager#saveFragmentInstanceState(Fragment) @@ -1389,6 +1393,38 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } /** + * Returns the cached LayoutInflater used to inflate Views of this Fragment. If + * {@link #onGetLayoutInflater(Bundle)} has not been called {@link #onGetLayoutInflater(Bundle)} + * will be called with a {@code null} argument and that value will be cached. + * <p> + * The cached LayoutInflater will be replaced immediately prior to + * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} and cleared immediately after + * {@link #onDetach()}. + * + * @return The LayoutInflater used to inflate Views of this Fragment. + */ + public final LayoutInflater getLayoutInflater() { + if (mLayoutInflater == null) { + return performGetLayoutInflater(null); + } + return mLayoutInflater; + } + + /** + * Calls {@link #onGetLayoutInflater(Bundle)} and caches the result for use by + * {@link #getLayoutInflater()}. + * + * @param savedInstanceState If the fragment is being re-created from + * a previous saved state, this is the state. + * @return The LayoutInflater used to inflate Views of this Fragment. + */ + LayoutInflater performGetLayoutInflater(Bundle savedInstanceState) { + LayoutInflater layoutInflater = onGetLayoutInflater(savedInstanceState); + mLayoutInflater = layoutInflater; + return mLayoutInflater; + } + + /** * @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead. */ @Deprecated @@ -2835,6 +2871,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene void performDetach() { mCalled = false; onDetach(); + mLayoutInflater = null; if (!mCalled) { throw new SuperNotCalledException("Fragment " + this + " did not call through to super.onDetach()"); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 91578a2da9c7..c1161a2eb0cf 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1259,7 +1259,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } f.mContainer = container; - f.mView = f.performCreateView(f.onGetLayoutInflater( + f.mView = f.performCreateView(f.performGetLayoutInflater( f.mSavedFragmentState), container, f.mSavedFragmentState); if (f.mView != null) { f.mView.setSaveFromParentEnabled(false); @@ -1431,7 +1431,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate void ensureInflatedFragmentView(Fragment f) { if (f.mFromLayout && !f.mPerformedCreateView) { - f.mView = f.performCreateView(f.onGetLayoutInflater( + f.mView = f.performCreateView(f.performGetLayoutInflater( f.mSavedFragmentState), null, f.mSavedFragmentState); if (f.mView != null) { f.mView.setSaveFromParentEnabled(false); @@ -1462,18 +1462,22 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate if (fragment.isHideReplaced()) { fragment.setHideReplaced(false); } else { + final ViewGroup container = fragment.mContainer; + final View animatingView = fragment.mView; + container.startViewTransition(animatingView); // Delay the actual hide operation until the animation finishes, otherwise // the fragment will just immediately disappear anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + container.endViewTransition(animatingView); animation.removeListener(this); - if (fragment.mView != null) { - fragment.mView.setVisibility(View.GONE); - } + animatingView.setVisibility(View.GONE); } }); } + } else { + fragment.mView.setVisibility(View.VISIBLE); } setHWLayerAnimListenerIfAlpha(fragment.mView, anim); anim.start(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 4210d2e7d3ec..d2702440f839 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -129,8 +129,7 @@ interface IActivityManager { void finishSubActivity(in IBinder token, in String resultWho, int requestCode); PendingIntent getRunningServiceControlPanel(in ComponentName service); ComponentName startService(in IApplicationThread caller, in Intent service, - in String resolvedType, int id, in Notification notification, - boolean requireForeground, in String callingPackage, int userId); + in String resolvedType, boolean requireForeground, in String callingPackage, int userId); int stopService(in IApplicationThread caller, in Intent service, in String resolvedType, int userId); int bindService(in IApplicationThread caller, in IBinder token, in Intent service, diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index f4e8f3f5687e..cd9c095890e7 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.pm.ParceledListSlice; import android.net.Uri; import android.os.Bundle; +import android.os.UserHandle; import android.service.notification.Adjustment; import android.service.notification.Condition; import android.service.notification.IConditionListener; @@ -101,9 +102,9 @@ interface INotificationManager void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); void setInterruptionFilter(String pkg, int interruptionFilter); - void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in NotificationChannel channel); - ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg); - ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg); + void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel); + ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user); + ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user); void applyEnqueuedAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 4205db098ef6..bbcf7ba59128 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1598,7 +1598,7 @@ public final class LoadedApk { mConnection.onServiceDisconnected(name); } if (dead) { - mConnection.onBindingDead(name); + mConnection.onBindingDied(name); } // If there is a new service, it is now connected. if (service != null) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8ed52a1bba59..602cccb1070d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3251,7 +3251,6 @@ public class Notification implements Parcelable * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications * that have a media session attached there is no such requirement. * - * @see Builder#setOngoing(boolean) * @see Builder#setColor(int) * @see MediaStyle#setMediaSession(MediaSession.Token) */ @@ -3742,6 +3741,11 @@ public class Notification implements Parcelable return mActionBarColor; } + private int getActionBarColorDeEmphasized() { + int backgroundColor = getBackgroundColor(); + return NotificationColorUtil.getShiftedColor(backgroundColor, 12); + } + private void setTextViewColorSecondary(RemoteViews contentView, int id) { ensureColors(); contentView.setTextColor(id, mSecondaryTextColor); @@ -4202,9 +4206,22 @@ public class Notification implements Parcelable * @hide */ public RemoteViews makePublicContentView() { + return makePublicView(false /* ambient */); + } + + /** + * Construct a RemoteViews for the display in public contexts like on the lockscreen. + * + * @hide + */ + public RemoteViews makePublicAmbientNotification() { + return makePublicView(true /* ambient */); + } + + private RemoteViews makePublicView(boolean ambient) { if (mN.publicVersion != null) { final Builder builder = recoverBuilder(mContext, mN.publicVersion); - return builder.createContentView(); + return ambient ? builder.makeAmbientNotification() : builder.createContentView(); } Bundle savedBundle = mN.extras; Style style = mStyle; @@ -4221,14 +4238,15 @@ public class Notification implements Parcelable publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN)); publicExtras.putCharSequence(EXTRA_TITLE, - mContext.getString(R.string.notification_hidden_text)); + mContext.getString(com.android.internal.R.string.notification_hidden_text)); mN.extras = publicExtras; - final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource()); + final RemoteViews view = ambient ? makeAmbientNotification() + : applyStandardTemplate(getBaseLayoutResource()); mN.extras = savedBundle; mN.mLargeIcon = largeIcon; mN.largeIcon = largeIconLegacy; mStyle = style; - return publicView; + return view; } /** @@ -4302,8 +4320,13 @@ public class Notification implements Parcelable // TODO: handle emphasized mode / actions right if (emphazisedMode) { // change the background bgColor - int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list - : R.color.notification_action_list_dark); + int bgColor; + if (isColorized()) { + bgColor = oddAction ? getActionBarColor() : getActionBarColorDeEmphasized(); + } else { + bgColor = mContext.getColor(oddAction ? R.color.notification_action_list + : R.color.notification_action_list_dark); + } button.setDrawableParameters(R.id.button_holder, true, -1, bgColor, PorterDuff.Mode.SRC_ATOP, -1); CharSequence title = action.title; diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 72c59781ae80..242d4a5e6267 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1155,40 +1155,4 @@ public class NotificationManager } } - /** - * Start a service directly into the "foreground service" state. Unlike - * {@link android.content.Context#startService(Intent)}, this method - * can be used from within background operations like broadcast receivers - * or scheduled jobs. - * - * @param service Description of the service to be started. The Intent must be either - * fully explicit (supplying a component name) or specify a specific package - * name it is targeted to. - * @param id The identifier for this notification as per - * {@link #notify(int, Notification) NotificationManager.notify(int, Notification)}; - * must not be 0. - * @param notification The Notification to be displayed. - * @return If the service is being started or is already running, the - * {@link ComponentName} of the actual service that was started is - * returned; else if the service does not exist null is returned. - * - * @deprecated STOPSHIP transition away from this for O - */ - @Nullable - @Deprecated - public ComponentName startServiceInForeground(Intent service, - int id, Notification notification) { - return mContext.startServiceInForeground(service, id, notification); - } - - /** - * @hide like {@link #startServiceInForeground(Intent, int, Notification)} - * but for a specific user. - */ - @Nullable - public ComponentName startServiceInForegroundAsUser(Intent service, - int id, Notification notification, UserHandle user) { - return mContext.startServiceInForegroundAsUser(service, id, notification, user); - } - } diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java index 0ce5eebed287..2fa636046901 100644 --- a/core/java/android/app/PictureInPictureArgs.java +++ b/core/java/android/app/PictureInPictureArgs.java @@ -49,6 +49,13 @@ public final class PictureInPictureArgs implements Parcelable { @Nullable private Rect mSourceRectHint; + /** + * The content insets that are used with the source hint rect for the transition into PiP where + * the insets are removed at the beginning of the transition. + */ + @Nullable + private Rect mSourceRectHintInsets; + PictureInPictureArgs(Parcel in) { if (in.readInt() != 0) { mAspectRatio = in.readFloat(); @@ -60,6 +67,9 @@ public final class PictureInPictureArgs implements Parcelable { if (in.readInt() != 0) { mSourceRectHint = Rect.CREATOR.createFromParcel(in); } + if (in.readInt() != 0) { + mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in); + } } /** @@ -94,6 +104,9 @@ public final class PictureInPictureArgs implements Parcelable { if (otherArgs.hasSourceBoundsHint()) { mSourceRectHint = new Rect(otherArgs.getSourceRectHint()); } + if (otherArgs.hasSourceBoundsHintInsets()) { + mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets()); + } } /** @@ -167,7 +180,19 @@ public final class PictureInPictureArgs implements Parcelable { } /** - * @return the launch bounds + * Sets the insets to be used with the source rect hint bounds. + * @hide + */ + public void setSourceRectHintInsets(Rect insets) { + if (insets == null) { + mSourceRectHintInsets = null; + } else { + mSourceRectHintInsets = new Rect(insets); + } + } + + /** + * @return the source rect hint * @hide */ public Rect getSourceRectHint() { @@ -175,6 +200,14 @@ public final class PictureInPictureArgs implements Parcelable { } /** + * @return the source rect hint insets. + * @hide + */ + public Rect getSourceRectHintInsets() { + return mSourceRectHintInsets; + } + + /** * @return whether there are launch bounds set * @hide */ @@ -182,12 +215,23 @@ public final class PictureInPictureArgs implements Parcelable { return mSourceRectHint != null && !mSourceRectHint.isEmpty(); } + /** + * @return whether there are source rect hint insets set + * @hide + */ + public boolean hasSourceBoundsHintInsets() { + return mSourceRectHintInsets != null; + } + @Override public PictureInPictureArgs clone() { PictureInPictureArgs args = new PictureInPictureArgs(mAspectRatio, mUserActions); if (mSourceRectHint != null) { args.setSourceRectHint(mSourceRectHint); } + if (mSourceRectHintInsets != null) { + args.setSourceRectHintInsets(mSourceRectHintInsets); + } return args; } @@ -216,6 +260,12 @@ public final class PictureInPictureArgs implements Parcelable { } else { out.writeInt(0); } + if (mSourceRectHintInsets != null) { + out.writeInt(1); + mSourceRectHintInsets.writeToParcel(out, 0); + } else { + out.writeInt(0); + } } public static final Creator<PictureInPictureArgs> CREATOR = diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 545aef5224b7..9f911f5c19c3 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -964,6 +964,16 @@ public class AssistStructure implements Parcelable { * * @return The hints for this view */ + @Nullable public String[] getAutofillHints() { + return mAutofillHints; + } + + /** + * @hide + * @deprecated use getAutofillHints() instead. + */ + // TODO(b/33197203): remove once clients don't use it anymore... + @Deprecated @Nullable public String[] getAutoFillHints() { return mAutofillHints; } diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java index 016a0fa8b92b..673d1b886b25 100644 --- a/core/java/android/app/job/JobParameters.java +++ b/core/java/android/app/job/JobParameters.java @@ -164,6 +164,20 @@ public class JobParameters implements Parcelable { * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time). * + * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call + * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done + * executing the work. The job will not be finished until all dequeued work has been + * completed. You do not, however, have to complete each returned work item before deqeueing + * the next one -- you can use {@link #dequeueWork()} multiple times before completing + * previous work if you want to process work in parallel, and you can complete the work + * in whatever order you want.</p> + * + * <p>If the job runs to the end of its available time period before all work has been + * completed, it will stop as normal. You should return true from + * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by + * doing so any pending as well as remaining uncompleted work will be re-queued + * for the next time the job runs.</p> + * * @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null. * If null is returned, the system will also stop the job if all work has also been completed. * (This means that for correct operation, you must always call dequeueWork() after you have diff --git a/core/java/android/app/job/JobServiceEngine.java b/core/java/android/app/job/JobServiceEngine.java index 879212ea1fb3..a62861902a55 100644 --- a/core/java/android/app/job/JobServiceEngine.java +++ b/core/java/android/app/job/JobServiceEngine.java @@ -54,7 +54,7 @@ public abstract class JobServiceEngine { /** * Context we are running in. */ - private final Context mContext; + private final Service mService; private final IJobService mBinder; @@ -182,12 +182,12 @@ public abstract class JobServiceEngine { /** * Create a new engine, ready for use. * - * @param context The {@link Service} that is creating this engine. + * @param service The {@link Service} that is creating this engine and in which it will run. */ - public JobServiceEngine(Context context) { - mContext = context; + public JobServiceEngine(Service service) { + mService = service; mBinder = new JobInterface(this); - mHandler = new JobHandler(mContext.getMainLooper()); + mHandler = new JobHandler(mService.getMainLooper()); } /** diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java index 4bb057e689b2..05687ee9aace 100644 --- a/core/java/android/app/job/JobWorkItem.java +++ b/core/java/android/app/job/JobWorkItem.java @@ -27,6 +27,7 @@ import android.os.Parcelable; final public class JobWorkItem implements Parcelable { final Intent mIntent; int mWorkId; + Object mGrants; /** * Create a new piece of work. @@ -57,6 +58,20 @@ final public class JobWorkItem implements Parcelable { return mWorkId; } + /** + * @hide + */ + public void setGrants(Object grants) { + mGrants = grants; + } + + /** + * @hide + */ + public Object getGrants() { + return mGrants; + } + public String toString() { return "JobWorkItem{id=" + mWorkId + " intent=" + mIntent + "}"; } diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java index 6fc4f5c1c95c..4b6479ad4a7b 100644 --- a/core/java/android/app/usage/StorageStatsManager.java +++ b/core/java/android/app/usage/StorageStatsManager.java @@ -16,17 +16,25 @@ package android.app.usage; +import static android.os.storage.StorageManager.convert; + +import android.annotation.NonNull; import android.annotation.TestApi; import android.annotation.WorkerThread; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.ParcelableException; import android.os.RemoteException; import android.os.UserHandle; +import android.os.storage.StorageManager; import com.android.internal.util.Preconditions; import java.io.File; +import java.io.IOException; +import java.util.UUID; /** * Provides access to detailed storage statistics. @@ -50,36 +58,49 @@ public class StorageStatsManager { /** {@hide} */ @TestApi - public boolean isQuotaSupported(String volumeUuid) { + public boolean isQuotaSupported(@NonNull UUID storageUuid) { try { - return mService.isQuotaSupported(volumeUuid, mContext.getOpPackageName()); + return mService.isQuotaSupported(convert(storageUuid), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public boolean isQuotaSupported(String uuid) { + return isQuotaSupported(convert(uuid)); + } + /** - * Return the total space on the requested storage volume. + * Return the total size of the media hosting this storage volume. * <p> - * To reduce end user confusion, this value is the total storage size + * To reduce end user confusion, this value matches the total storage size * advertised in a retail environment, which is typically larger than the - * actual writable partition total size. - * <p> - * This method may take several seconds to calculate the requested values, - * so it should only be called from a worker thread. + * actual usable partition space. * - * @param volumeUuid the UUID of the storage volume you're interested in, or - * {@code null} to specify the default internal storage. + * @param storageUuid the UUID of the storage volume you're interested in, + * such as {@link StorageManager#UUID_DEFAULT}. + * @throws IOException when the storage device isn't present. */ @WorkerThread - public long getTotalBytes(String volumeUuid) { + public long getTotalBytes(@NonNull UUID storageUuid) throws IOException { try { - return mService.getTotalBytes(volumeUuid, mContext.getOpPackageName()); + return mService.getTotalBytes(convert(storageUuid), mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public long getTotalBytes(String uuid) throws IOException { + return getTotalBytes(convert(uuid)); + } + /** * Return the free space on the requested storage volume. * <p> @@ -90,18 +111,28 @@ public class StorageStatsManager { * This method may take several seconds to calculate the requested values, * so it should only be called from a worker thread. * - * @param volumeUuid the UUID of the storage volume you're interested in, or - * {@code null} to specify the default internal storage. + * @param storageUuid the UUID of the storage volume you're interested in, + * such as {@link StorageManager#UUID_DEFAULT}. + * @throws IOException when the storage device isn't present. */ @WorkerThread - public long getFreeBytes(String volumeUuid) { + public long getFreeBytes(@NonNull UUID storageUuid) throws IOException { try { - return mService.getFreeBytes(volumeUuid, mContext.getOpPackageName()); + return mService.getFreeBytes(convert(storageUuid), mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public long getFreeBytes(String uuid) throws IOException { + return getFreeBytes(convert(uuid)); + } + /** * Return storage statistics for a specific package on the requested storage * volume. @@ -112,27 +143,41 @@ public class StorageStatsManager { * Note: if the requested package uses the {@code android:sharedUserId} * manifest feature, this call will be forced into a slower manual * calculation path. If possible, consider always using - * {@link #queryStatsForUid(String, int)}, which is typically faster. + * {@link #queryStatsForUid(UUID, int)}, which is typically faster. * </p> * - * @param volumeUuid the UUID of the storage volume you're interested in, or - * {@code null} to specify the default internal storage. + * @param storageUuid the UUID of the storage volume you're interested in, + * such as {@link StorageManager#UUID_DEFAULT}. * @param packageName the package name you're interested in. * @param user the user you're interested in. - * @see ApplicationInfo#volumeUuid + * @throws PackageManager.NameNotFoundException when the requested package + * name isn't installed for the requested user. + * @throws IOException when the storage device isn't present. + * @see ApplicationInfo#storageUuid * @see PackageInfo#packageName */ @WorkerThread - public StorageStats queryStatsForPackage(String volumeUuid, String packageName, - UserHandle user) { + public @NonNull StorageStats queryStatsForPackage(@NonNull UUID storageUuid, String packageName, + UserHandle user) throws PackageManager.NameNotFoundException, IOException { try { - return mService.queryStatsForPackage(volumeUuid, packageName, user.getIdentifier(), - mContext.getOpPackageName()); + return mService.queryStatsForPackage(convert(storageUuid), packageName, + user.getIdentifier(), mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(PackageManager.NameNotFoundException.class); + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public StorageStats queryStatsForPackage(String uuid, String packageName, + UserHandle user) throws PackageManager.NameNotFoundException, IOException { + return queryStatsForPackage(convert(uuid), packageName, user); + } + /** * Return storage statistics for a specific UID on the requested storage * volume. @@ -140,21 +185,32 @@ public class StorageStatsManager { * This method may take several seconds to calculate the requested values, * so it should only be called from a worker thread. * - * @param volumeUuid the UUID of the storage volume you're interested in, or - * {@code null} to specify the default internal storage. + * @param storageUuid the UUID of the storage volume you're interested in, + * such as {@link StorageManager#UUID_DEFAULT}. * @param uid the UID you're interested in. - * @see ApplicationInfo#volumeUuid + * @throws IOException when the storage device isn't present. + * @see ApplicationInfo#storageUuid * @see ApplicationInfo#uid */ @WorkerThread - public StorageStats queryStatsForUid(String volumeUuid, int uid) { + public StorageStats queryStatsForUid(@NonNull UUID storageUuid, int uid) throws IOException { try { - return mService.queryStatsForUid(volumeUuid, uid, mContext.getOpPackageName()); + return mService.queryStatsForUid(convert(storageUuid), uid, + mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public StorageStats queryStatsForUid(String uuid, int uid) throws IOException { + return queryStatsForUid(convert(uuid), uid); + } + /** * Return storage statistics for a specific {@link UserHandle} on the * requested storage volume. @@ -162,21 +218,32 @@ public class StorageStatsManager { * This method may take several seconds to calculate the requested values, * so it should only be called from a worker thread. * - * @param volumeUuid the UUID of the storage volume you're interested in, or - * {@code null} to specify the default internal storage. + * @param storageUuid the UUID of the storage volume you're interested in, + * such as {@link StorageManager#UUID_DEFAULT}. * @param user the user you're interested in. + * @throws IOException when the storage device isn't present. * @see android.os.Process#myUserHandle() */ @WorkerThread - public StorageStats queryStatsForUser(String volumeUuid, UserHandle user) { + public StorageStats queryStatsForUser(@NonNull UUID storageUuid, UserHandle user) + throws IOException { try { - return mService.queryStatsForUser(volumeUuid, user.getIdentifier(), + return mService.queryStatsForUser(convert(storageUuid), user.getIdentifier(), mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public StorageStats queryStatsForUser(String uuid, UserHandle user) throws IOException { + return queryStatsForUser(convert(uuid), user); + } + /** * Return shared/external storage statistics for a specific * {@link UserHandle} on the requested storage volume. @@ -184,20 +251,32 @@ public class StorageStatsManager { * This method may take several seconds to calculate the requested values, * so it should only be called from a worker thread. * - * @param volumeUuid the UUID of the storage volume you're interested in, or - * {@code null} to specify the default internal storage. + * @param storageUuid the UUID of the storage volume you're interested in, + * such as {@link StorageManager#UUID_DEFAULT}. + * @throws IOException when the storage device isn't present. * @see android.os.Process#myUserHandle() */ @WorkerThread - public ExternalStorageStats queryExternalStatsForUser(String volumeUuid, UserHandle user) { + public ExternalStorageStats queryExternalStatsForUser(@NonNull UUID storageUuid, + UserHandle user) throws IOException { try { - return mService.queryExternalStatsForUser(volumeUuid, user.getIdentifier(), + return mService.queryExternalStatsForUser(convert(storageUuid), user.getIdentifier(), mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public ExternalStorageStats queryExternalStatsForUser(String uuid, UserHandle user) + throws IOException { + return queryExternalStatsForUser(convert(uuid), user); + } + /** {@hide} */ public long getCacheQuotaBytes(String volumeUuid, int uid) { try { diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 6ba52b70d1ab..c1ff580cbce7 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -276,8 +276,6 @@ public class AppWidgetHost { /** * Gets a list of all the appWidgetIds that are bound to the current host - * - * @hide */ public int[] getAppWidgetIds() { try { diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index ef3b1c50ffd3..624ec871a15a 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -498,8 +498,13 @@ public class AppWidgetHostView extends FrameLayout { private void updateContentDescription(AppWidgetProviderInfo info) { if (info != null) { LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class); - ApplicationInfo appInfo = launcherApps.getApplicationInfo( - info.provider.getPackageName(), 0, info.getProfile()); + ApplicationInfo appInfo = null; + try { + appInfo = launcherApps.getApplicationInfo( + info.provider.getPackageName(), 0, info.getProfile()); + } catch (NameNotFoundException e) { + // ignore -- use null. + } if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0) { setContentDescription( diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 18120c714793..46a704258c1a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -57,6 +57,7 @@ import android.os.Looper; import android.os.StatFs; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.StorageManager; import android.provider.MediaStore; import android.util.AttributeSet; import android.view.Display; @@ -1146,16 +1147,26 @@ public abstract class Context { /** * Returns the absolute path to the application specific cache directory on - * the filesystem. These files will be ones that get deleted first when the - * device runs low on storage. There is no guarantee when these files will - * be deleted. + * the filesystem. * <p> - * <strong>Note: you should not <em>rely</em> on the system deleting these - * files for you; you should always have a reasonable maximum, such as 1 MB, - * for the amount of space you consume with cache files, and prune those - * files when exceeding that space.</strong> If your app requires a larger - * cache (larger than 1 MB), you should use {@link #getExternalCacheDir()} - * instead. + * The system will automatically delete files in this directory as disk + * space is needed elsewhere on the device. The system will always delete + * older files first, as reported by {@link File#lastModified()}. If + * desired, you can exert more control over how files are deleted using + * {@link StorageManager#setCacheBehaviorGroup(File, boolean)} and + * {@link StorageManager#setCacheBehaviorTombstone(File, boolean)}. + * <p> + * Apps are strongly encouraged to keep their usage of cache space below the + * quota returned by + * {@link StorageManager#getCacheQuotaBytes(java.util.UUID)}. If your app + * goes above this quota, your cached files will be some of the first to be + * deleted when additional disk space is needed. Conversely, if your app + * stays under this quota, your cached files will be some of the last to be + * deleted when additional disk space is needed. + * <p> + * Note that your cache quota will change over time depending on how + * frequently the user interacts with your app, and depending on how much + * system-wide disk space is used. * <p> * The returned path may change over time if the calling app is moved to an * adopted storage device, so only relative paths should be persisted. @@ -1173,9 +1184,11 @@ public abstract class Context { /** * Returns the absolute path to the application specific cache directory on - * the filesystem designed for storing cached code. The system will delete - * any files stored in this location both when your specific application is - * upgraded, and when the entire platform is upgraded. + * the filesystem designed for storing cached code. + * <p> + * The system will delete any files stored in this location both when your + * specific application is upgraded, and when the entire platform is + * upgraded. * <p> * This location is optimal for storing compiled or optimized code generated * by your application at runtime. @@ -2647,18 +2660,6 @@ public abstract class Context { public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user); /** - * Start a service directly into the "foreground service" state. Unlike {@link #startService}, - * this method can be used from within background operations like broadcast receivers - * or scheduled jobs. The API entry point for this is in NotificationManager in order to - * preserve appropriate public package layering. - * @hide - * @deprecated STOPSHIP remove in favor of two-step startForegroundService() + startForeground() - */ - @Nullable - public abstract ComponentName startServiceInForeground(Intent service, - int id, Notification notification); - - /** * Request that a given application service be stopped. If the service is * not running, nothing happens. Otherwise it is stopped. Note that calls * to startService() are not counted -- this stops the service no matter @@ -2696,16 +2697,6 @@ public abstract class Context { public abstract ComponentName startServiceAsUser(Intent service, UserHandle user); /** - * @hide like {@link #startServiceInForeground(Intent, int, Notification)} - * but for a specific user. - * @deprecated STOPSHIP remove when trial API is turned off - */ - @Deprecated - @Nullable - public abstract ComponentName startServiceInForegroundAsUser(Intent service, - int id, Notification notification, UserHandle user); - - /** * @hide like {@link #stopService(Intent)} but for a specific user. */ public abstract boolean stopServiceAsUser(Intent service, UserHandle user); @@ -3744,6 +3735,7 @@ public abstract class Context { public static final String PRINT_SERVICE = "print"; /** + * Use with {@link #getSystemService} to retrieve a * {@link android.companion.CompanionDeviceManager} for managing companion devices * * @see #getSystemService diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 53b021cbdf9f..b59fc3ddbb84 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -649,13 +649,6 @@ public class ContextWrapper extends Context { return mBase.startForegroundService(service); } - /** @hide STOPSHIP remove when trial API is turned down */ - @Override - public ComponentName startServiceInForeground(Intent service, - int id, Notification notification) { - return mBase.startServiceInForeground(service, id, notification); - } - @Override public boolean stopService(Intent name) { return mBase.stopService(name); @@ -673,13 +666,6 @@ public class ContextWrapper extends Context { return mBase.startForegroundServiceAsUser(service, user); } - /** @hide STOPSHIP removed when trial API is turned down */ - @Override - public ComponentName startServiceInForegroundAsUser(Intent service, - int id, Notification notification, UserHandle user) { - return mBase.startServiceInForegroundAsUser(service, id, notification, user); - } - /** @hide */ @Override public boolean stopServiceAsUser(Intent name, UserHandle user) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5ca4fa343271..3e3f1ee5ef56 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1517,7 +1517,7 @@ public class Intent implements Parcelable, Cloneable { * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install * succeeded. * <p> - * <strong>Note:</strong>If your app is targeting API level higher than 22 you + * <strong>Note:</strong>If your app is targeting API level higher than 25 you * need to hold {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} * in order to launch the application installer. * </p> @@ -1656,6 +1656,8 @@ public class Intent implements Parcelable, Cloneable { /** * Used as an int extra field with {@link #ACTION_INSTALL_PACKAGE} and * {@link #ACTION_VIEW} to indicate the uid of the package that initiated the install + * Currently only a system app that hosts the provider authority "downloads" or holds the + * permission {@link android.Manifest.permission.MANAGE_DOCUMENTS} can use this. * @hide */ @SystemApi @@ -2072,37 +2074,21 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; /** - * Broadcast Action: An application package has been installed or updated on the - * device. The data contains the name of the package. Note that the - * newly installed package does <em>not</em> receive this broadcast. - * <p>May include the following extras: - * <ul> - * <li> {@link #EXTRA_UID} containing the integer uid assigned to this package. - * <li> {@link #EXTRA_REPLACING} is set to {@code true} if this is following - * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package. - * </ul> - * - * <p class="note">This is a protected intent that can only be sent - * by the system. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; - /** * Broadcast Action: A new application package has been installed on the * device. The data contains the name of the package. Note that the * newly installed package does <em>not</em> receive this broadcast. - * <p class="note">Unlike {@link #ACTION_PACKAGE_ADDED}, this broadcast is delivered - * to manifest receivers as well as those registered at runtime. * <p>May include the following extras: * <ul> * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package. + * <li> {@link #EXTRA_REPLACING} is set to true if this is following + * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package. * </ul> * * <p class="note">This is a protected intent that can only be sent * by the system. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED"; + public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; /** * Broadcast Action: A new version of an application package has been * installed, replacing an existing version that was previously installed. @@ -8616,6 +8602,32 @@ public class Intent implements Parcelable, Cloneable { * a single statement. * @see #setFlags(int) * @see #removeFlags(int) + * + * @see #FLAG_GRANT_READ_URI_PERMISSION + * @see #FLAG_GRANT_WRITE_URI_PERMISSION + * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION + * @see #FLAG_GRANT_PREFIX_URI_PERMISSION + * @see #FLAG_DEBUG_LOG_RESOLUTION + * @see #FLAG_FROM_BACKGROUND + * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT + * @see #FLAG_ACTIVITY_CLEAR_TASK + * @see #FLAG_ACTIVITY_CLEAR_TOP + * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET + * @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + * @see #FLAG_ACTIVITY_FORWARD_RESULT + * @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY + * @see #FLAG_ACTIVITY_MULTIPLE_TASK + * @see #FLAG_ACTIVITY_NEW_DOCUMENT + * @see #FLAG_ACTIVITY_NEW_TASK + * @see #FLAG_ACTIVITY_NO_ANIMATION + * @see #FLAG_ACTIVITY_NO_HISTORY + * @see #FLAG_ACTIVITY_NO_USER_ACTION + * @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP + * @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + * @see #FLAG_ACTIVITY_REORDER_TO_FRONT + * @see #FLAG_ACTIVITY_SINGLE_TOP + * @see #FLAG_ACTIVITY_TASK_ON_HOME + * @see #FLAG_RECEIVER_REGISTERED_ONLY */ public Intent addFlags(int flags) { mFlags |= flags; @@ -8628,6 +8640,32 @@ public class Intent implements Parcelable, Cloneable { * @param flags The flags to remove. * @see #setFlags(int) * @see #addFlags(int) + * + * @see #FLAG_GRANT_READ_URI_PERMISSION + * @see #FLAG_GRANT_WRITE_URI_PERMISSION + * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION + * @see #FLAG_GRANT_PREFIX_URI_PERMISSION + * @see #FLAG_DEBUG_LOG_RESOLUTION + * @see #FLAG_FROM_BACKGROUND + * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT + * @see #FLAG_ACTIVITY_CLEAR_TASK + * @see #FLAG_ACTIVITY_CLEAR_TOP + * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET + * @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + * @see #FLAG_ACTIVITY_FORWARD_RESULT + * @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY + * @see #FLAG_ACTIVITY_MULTIPLE_TASK + * @see #FLAG_ACTIVITY_NEW_DOCUMENT + * @see #FLAG_ACTIVITY_NEW_TASK + * @see #FLAG_ACTIVITY_NO_ANIMATION + * @see #FLAG_ACTIVITY_NO_HISTORY + * @see #FLAG_ACTIVITY_NO_USER_ACTION + * @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP + * @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + * @see #FLAG_ACTIVITY_REORDER_TO_FRONT + * @see #FLAG_ACTIVITY_SINGLE_TOP + * @see #FLAG_ACTIVITY_TASK_ON_HOME + * @see #FLAG_RECEIVER_REGISTERED_ONLY */ public void removeFlags(int flags) { mFlags &= ~flags; diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 23e54baab622..d64f018c3f2e 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1787,7 +1787,7 @@ public class IntentFilter implements Parcelable { sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes); du.println(sb.toString()); } - { + if (getAutoVerify()) { sb.setLength(0); sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify()); du.println(sb.toString()); diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java index 8e428f9e9b3d..6ff4900212ff 100644 --- a/core/java/android/content/ServiceConnection.java +++ b/core/java/android/content/ServiceConnection.java @@ -61,6 +61,6 @@ public interface ServiceConnection { * @param name The concrete component name of the service whose * connection is dead. */ - default void onBindingDead(ComponentName name) { + default void onBindingDied(ComponentName name) { } } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 5f53e27eeb07..b0f8aa7ec6aa 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -29,6 +29,7 @@ import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.text.TextUtils; import android.util.Printer; import android.util.SparseArray; @@ -41,6 +42,7 @@ import java.text.Collator; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; +import java.util.UUID; /** * Information you can retrieve about a particular application. This @@ -621,12 +623,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public float maxAspectRatio; + /** @removed */ + @Deprecated + public String volumeUuid; + /** * UUID of the storage volume on which this application is being hosted. For * apps hosted on the default internal storage at - * {@link Environment#getDataDirectory()}, the UUID value is {@code null}. + * {@link Environment#getDataDirectory()}, the UUID value is + * {@link StorageManager#UUID_DEFAULT}. */ - public String volumeUuid; + public UUID storageUuid; /** {@hide} */ public String scanSourceDir; @@ -1134,6 +1141,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { compatibleWidthLimitDp = orig.compatibleWidthLimitDp; largestWidthLimitDp = orig.largestWidthLimitDp; volumeUuid = orig.volumeUuid; + storageUuid = orig.storageUuid; scanSourceDir = orig.scanSourceDir; scanPublicSourceDir = orig.scanPublicSourceDir; sourceDir = orig.sourceDir; @@ -1195,7 +1203,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(requiresSmallestWidthDp); dest.writeInt(compatibleWidthLimitDp); dest.writeInt(largestWidthLimitDp); - dest.writeString(volumeUuid); + if (storageUuid != null) { + dest.writeInt(1); + dest.writeUuid(storageUuid); + } else { + dest.writeInt(0); + } dest.writeString(scanSourceDir); dest.writeString(scanPublicSourceDir); dest.writeString(sourceDir); @@ -1257,7 +1270,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { requiresSmallestWidthDp = source.readInt(); compatibleWidthLimitDp = source.readInt(); largestWidthLimitDp = source.readInt(); - volumeUuid = source.readString(); + if (source.readInt() != 0) { + storageUuid = source.readUuid(); + volumeUuid = StorageManager.convert(storageUuid); + } scanSourceDir = source.readString(); scanPublicSourceDir = source.readString(); sourceDir = source.readString(); diff --git a/core/java/android/content/pm/ChangedPackages.java b/core/java/android/content/pm/ChangedPackages.java index 94b8a5dfd5e0..78c057d9e0f4 100644 --- a/core/java/android/content/pm/ChangedPackages.java +++ b/core/java/android/content/pm/ChangedPackages.java @@ -17,6 +17,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; @@ -26,6 +27,7 @@ import java.util.List; /** * Packages that have been changed since the last time they * were requested. + * @see PackageManager#getChangedPackages(int) */ public final class ChangedPackages implements Parcelable { /** The last known sequence number for these changes */ @@ -33,6 +35,7 @@ public final class ChangedPackages implements Parcelable { /** The names of the packages that have changed */ private final List<String> mPackageNames; + @TestApi public ChangedPackages(int sequenceNumber, @NonNull List<String> packageNames) { this.mSequenceNumber = sequenceNumber; this.mPackageNames = packageNames; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index bbc942aff372..bc7a61200e8b 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -634,4 +634,6 @@ interface IPackageManager { ComponentName getInstantAppResolverSettingsComponent(); ComponentName getInstantAppInstallerComponent(); + + String getInstantAppAndroidId(String packageName, int userId); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index c3bdde51af0f..8ead0ec612f8 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -22,6 +22,8 @@ import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.TestApi; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -52,6 +54,8 @@ import android.os.UserManager; import android.util.DisplayMetrics; import android.util.Log; +import com.android.internal.util.Preconditions; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -612,11 +616,20 @@ public class LauncherApps { * null if the package isn't installed for the given user, or the target user * is not enabled. */ - public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags, - UserHandle user) { + public ApplicationInfo getApplicationInfo(@NonNull String packageName, + @ApplicationInfoFlags int flags, @NonNull UserHandle user) + throws PackageManager.NameNotFoundException { + Preconditions.checkNotNull(packageName, "packageName"); + Preconditions.checkNotNull(packageName, "user"); logErrorForInvalidProfileAccess(user); try { - return mService.getApplicationInfo(mContext.getPackageName(), packageName, flags, user); + final ApplicationInfo ai = mService + .getApplicationInfo(mContext.getPackageName(), packageName, flags, user); + if (ai == null) { + throw new NameNotFoundException("Package " + packageName + " not found for user " + + user.getIdentifier()); + } + return ai; } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -1268,15 +1281,34 @@ public class LauncherApps { * an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent * respectively to the default launcher app. * - * <p>Note the launcher may receive a request to pin a shortcut that is already pinned, because - * the user may actually want to have multiple icons of the same shortcut on the launcher. - * The launcher can tell this case by calling {@link ShortcutInfo#isPinned()} on the shortcut - * returned by {@link #getShortcutInfo()}. In this case, calling {@link #accept()} is optional; - * even if the launcher does not call it, the shortcut is already pinned. Also in this case, - * the {@code options} argument to {@link #accept(Bundle)} will be ignored. + * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type. + * + * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a + * {@link ShortcutInfo}. If the launcher accepts a request, call {@link #accept()}, + * or {@link #accept(Bundle)} with a null or empty Bundle. No options are defined for + * pin-shortcuts requests. + * + * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type. + * + * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in + * which case {@link ShortcutInfo#isPinned()} returns true. This means the user wants to create + * another pinned shortcut for a shortcut that's already pinned. If the launcher accepts it, + * {@link #accept()} must still be called even though the shortcut is already pinned, and + * create a new pinned shortcut icon for it. + * + * <p>See also {@link ShortcutManager} for more details. + * + * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type. + * + * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a + * an AppWidget. If the launcher accepts a request, call {@link #accept(Bundle)} with + * the appwidget integer ID set to the + * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra. + * + * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null + * {@link AppWidgetProviderInfo} for this type. * - * <p>For AppWidget pin requests launcher should send back the appwidget id as an extra for - * {@link #accept(Bundle)} as {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID}. + * <p>See also {@link AppWidgetManager} for more details. * * @see #EXTRA_PIN_ITEM_REQUEST * @see #getPinItemRequest(Intent) @@ -1306,8 +1338,9 @@ public class LauncherApps { } /** - * Represents the type of a request. For now {@link #REQUEST_TYPE_SHORTCUT} is the only - * valid type. + * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants. + * + * @return one of the {@code REQUEST_TYPE_} constants. */ @RequestType public int getRequestType() { @@ -1315,8 +1348,12 @@ public class LauncherApps { } /** - * {@link ShortcutInfo} sent by the requesting app. Always non-null for a - * {@link #REQUEST_TYPE_SHORTCUT} request. + * {@link ShortcutInfo} sent by the requesting app. + * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a + * different request type. + * + * @return requested {@link ShortcutInfo} when a request is of the + * {@link #REQUEST_TYPE_SHORTCUT} type. Null otherwise. */ @Nullable public ShortcutInfo getShortcutInfo() { @@ -1328,8 +1365,12 @@ public class LauncherApps { } /** - * {@link AppWidgetProviderInfo} sent by the requesting app. Always non-null for a - * {@link #REQUEST_TYPE_APPWIDGET} request. + * {@link AppWidgetProviderInfo} sent by the requesting app. + * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a + * different request type. + * + * @return requested {@link AppWidgetProviderInfo} when a request is of the + * {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise. */ @Nullable public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) { @@ -1347,6 +1388,11 @@ public class LauncherApps { /** * Any extras sent by the requesting app. + * + * @return For a shortcut request, this method always return null. For an AppWidget + * request, this method returns the extras passed to the + * {@link android.appwidget.AppWidgetManager#requestPinAppWidget( + * ComponentName, Bundle, PendingIntent)} API. See {@link AppWidgetManager} for details. */ @Nullable public Bundle getExtras() { @@ -1358,8 +1404,9 @@ public class LauncherApps { } /** - * Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been - * called yet. + * Return whether a request is still valid. + * + * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called. */ public boolean isValid() { try { @@ -1371,6 +1418,12 @@ public class LauncherApps { /** * Called by the receiving launcher app when the user accepts the request. + * + * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request. + * + * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. + * {@code FALSE} if the item hasn't been pinned, for example, because the request had + * already been canceled, in which case the launcher must not pin the requested item. */ public boolean accept(@Nullable Bundle options) { try { @@ -1381,7 +1434,11 @@ public class LauncherApps { } /** - * Same as as {@link #accept(Bundle)} with no options. + * Called by the receiving launcher app when the user accepts the request, with no options. + * + * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned. + * {@code FALSE} if the item hasn't been pinned, for example, because the request had + * already been canceled, in which case the launcher must not pin the requested item. */ public boolean accept() { return accept(/* options= */ null); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 09906be7cd26..584f6d55e285 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -54,6 +54,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.provider.Settings; import android.util.AndroidException; import android.util.Log; @@ -2823,7 +2824,7 @@ public abstract class PackageManager { * by passing {@link #VERSION_CODE_HIGHEST} in the {@link VersionedPackage} * constructor. * - * @param versionedPackage The versioned packages for which to query. + * @param versionedPackage The versioned package for which to query. * @param flags Additional option flags. Use any combination of * {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS}, * {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION}, @@ -3845,9 +3846,9 @@ public abstract class PackageManager { * @return Whether caller is an instant app. * * @see #isInstantApp(String) - * @see #setInstantAppCookie(byte[]) + * @see #updateInstantAppCookie(byte[]) * @see #getInstantAppCookie() - * @see #getInstantAppCookieMaxSize() + * @see #getInstantAppCookieMaxBytes() */ public abstract boolean isInstantApp(); @@ -3858,9 +3859,10 @@ public abstract class PackageManager { * @return Whether the given package is an instant app. * * @see #isInstantApp() - * @see #setInstantAppCookie(byte[]) + * @see #updateInstantAppCookie(byte[]) * @see #getInstantAppCookie() - * @see #getInstantAppCookieMaxSize() + * @see #getInstantAppCookieMaxBytes() + * @see #clearInstantAppCookie() */ public abstract boolean isInstantApp(String packageName); @@ -3872,8 +3874,15 @@ public abstract class PackageManager { * * @see #isInstantApp() * @see #isInstantApp(String) - * @see #setInstantAppCookie(byte[]) + * @see #updateInstantAppCookie(byte[]) * @see #getInstantAppCookie() + * @see #clearInstantAppCookie() + */ + public abstract int getInstantAppCookieMaxBytes(); + + /** + * @deprecated + * @hide */ public abstract int getInstantAppCookieMaxSize(); @@ -3881,7 +3890,7 @@ public abstract class PackageManager { * Gets the instant application cookie for this app. Non * instant apps and apps that were instant but were upgraded * to normal apps can still access this API. For instant apps - * this cooke is cached for some time after uninstall while for + * this cookie is cached for some time after uninstall while for * normal apps the cookie is deleted after the app is uninstalled. * The cookie is always present while the app is installed. * @@ -3889,31 +3898,49 @@ public abstract class PackageManager { * * @see #isInstantApp() * @see #isInstantApp(String) - * @see #setInstantAppCookie(byte[]) - * @see #getInstantAppCookieMaxSize() + * @see #updateInstantAppCookie(byte[]) + * @see #getInstantAppCookieMaxBytes() + * @see #clearInstantAppCookie() */ public abstract @NonNull byte[] getInstantAppCookie(); /** - * Sets the instant application cookie for the calling app. Non + * Clears the instant application cookie for the calling app. + * + * @see #isInstantApp() + * @see #isInstantApp(String) + * @see #getInstantAppCookieMaxBytes() + * @see #getInstantAppCookie() + * @see #clearInstantAppCookie() + */ + public abstract void clearInstantAppCookie(); + + /** + * Updates the instant application cookie for the calling app. Non * instant apps and apps that were instant but were upgraded * to normal apps can still access this API. For instant apps - * this cooke is cached for some time after uninstall while for + * this cookie is cached for some time after uninstall while for * normal apps the cookie is deleted after the app is uninstalled. * The cookie is always present while the app is installed. The - * cookie size is limited by {@link #getInstantAppCookieMaxSize()}. - * If the provided cookie size is over the limit this method - * returns <code>false</code>. Passing <code>null</code> or an empty - * array clears the cookie. + * cookie size is limited by {@link #getInstantAppCookieMaxBytes()}. + * Passing <code>null</code> or an empty array clears the cookie. * </p> * * @param cookie The cookie data. - * @return Whether the cookie was set. * * @see #isInstantApp() * @see #isInstantApp(String) - * @see #getInstantAppCookieMaxSize() + * @see #getInstantAppCookieMaxBytes() * @see #getInstantAppCookie() + * @see #clearInstantAppCookie() + * + * @throws IllegalArgumentException if the array exceeds max cookie size. + */ + public abstract void updateInstantAppCookie(@Nullable byte[] cookie); + + /** + * @removed + * @hide */ public abstract boolean setInstantAppCookie(@Nullable byte[] cookie); @@ -3933,9 +3960,6 @@ public abstract class PackageManager { * @param flags To filter the libraries to return. * @return The shared library list. * - * @see #MATCH_FACTORY_ONLY - * @see #MATCH_KNOWN_PACKAGES - * @see #MATCH_ANY_USER * @see #MATCH_UNINSTALLED_PACKAGES */ public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries( @@ -3983,6 +4007,8 @@ public abstract class PackageManager { * <p>If no packages have been changed, returns <code>null</code>. * <p>The sequence number starts at <code>0</code> and is * reset every boot. + * @param sequenceNumber The first sequence number for which to retrieve package changes. + * @see Settings.Global#BOOT_COUNT */ public abstract @Nullable ChangedPackages getChangedPackages( @IntRange(from=0) int sequenceNumber); @@ -5937,8 +5963,20 @@ public abstract class PackageManager { * <p> * This hint can only be set by the app which installed this package, as * determined by {@link #getInstallerPackageName(String)}. - */ - public abstract void setApplicationCategoryHint(String packageName, + * + * @param packageName the package to change the category hint for. + * @param categoryHint the category hint to set; one of + * {@link ApplicationInfo#CATEGORY_AUDIO}, + * {@link ApplicationInfo#CATEGORY_GAME}, + * {@link ApplicationInfo#CATEGORY_IMAGE}, + * {@link ApplicationInfo#CATEGORY_MAPS}, + * {@link ApplicationInfo#CATEGORY_NEWS}, + * {@link ApplicationInfo#CATEGORY_PRODUCTIVITY}, + * {@link ApplicationInfo#CATEGORY_SOCIAL}, + * {@link ApplicationInfo#CATEGORY_UNDEFINED}, or + * {@link ApplicationInfo#CATEGORY_VIDEO}. + */ + public abstract void setApplicationCategoryHint(@NonNull String packageName, @ApplicationInfo.Category int categoryHint); /** {@hide} */ @@ -6256,18 +6294,18 @@ public abstract class PackageManager { /** * Checks whether the calling package is allowed to request package installs through package - * installer. Apps are encouraged to call this api before launching the package installer via + * installer. Apps are encouraged to call this API before launching the package installer via * intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the * user can explicitly choose what external sources they trust to install apps on the device. - * If this api returns false, the install request will be blocked by the package installer and + * If this API returns false, the install request will be blocked by the package installer and * a dialog will be shown to the user with an option to launch settings to change their * preference. An application must target Android O or higher and declare permission - * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this api. + * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this API. * * @return true if the calling package is trusted by the user to request install packages on * the device, false otherwise. - * @see {@link android.content.Intent#ACTION_INSTALL_PACKAGE} - * @see {@link android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES} + * @see android.content.Intent#ACTION_INSTALL_PACKAGE + * @see android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES */ public abstract boolean canRequestPackageInstalls(); @@ -6290,4 +6328,12 @@ public abstract class PackageManager { */ @SystemApi public abstract ComponentName getInstantAppInstallerComponent(); + + /** + * Return the Android Id for a given Instant App. + * + * @see {@link android.provider.Settings.Secure#ANDROID_ID} + * @hide + */ + public abstract String getInstantAppAndroidId(String packageName, @NonNull UserHandle user); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 430d8b15216f..1f78bff9e1a7 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -64,8 +64,10 @@ import android.os.FileUtils; import android.os.Parcel; import android.os.Parcelable; import android.os.PatternMatcher; +import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.OsConstants; import android.system.StructStat; @@ -116,6 +118,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; @@ -2111,6 +2114,12 @@ public class PackageParser { pkg.mIsStaticOverlay = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic, false); + final String propName = sa.getString( + com.android.internal.R.styleable + .AndroidManifestResourceOverlay_requiredSystemPropertyName); + final String propValue = sa.getString( + com.android.internal.R.styleable + .AndroidManifestResourceOverlay_requiredSystemPropertyValue); sa.recycle(); if (pkg.mOverlayTarget == null) { @@ -2118,15 +2127,22 @@ public class PackageParser { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } + if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) { outError[0] = "<overlay> priority must be between 0 and 9999"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } - if (pkg.mIsStaticOverlay) { - // TODO(b/35742444): Need to support selection method based on a package name. + + // check to see if overlay should be excluded based on system property condition + if (!checkOverlayRequiredSystemProperty(propName, propValue)) { + Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and " + + pkg.baseCodePath+ ": overlay ignored due to required system property: " + + propName + " with value: " + propValue); + return null; } + XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_KEY_SETS)) { @@ -2531,6 +2547,25 @@ public class PackageParser { return pkg; } + private boolean checkOverlayRequiredSystemProperty(String propName, String propValue) { + + if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) { + if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) { + // malformed condition - incomplete + Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName + + "=" + propValue + "' - require both requiredSystemPropertyName" + + " AND requiredSystemPropertyValue to be specified."); + return false; + } + // no valid condition set - so no exclusion criteria, overlay will be included. + return true; + } + + // check property value - make sure it is both set and equal to expected value + final String currValue = SystemProperties.get(propName); + return (currValue != null && currValue.equals(propValue)); + } + /** * This is a pre-density application which will get scaled - instead of being pixel perfect. * This type of application is not resizable. @@ -5741,11 +5776,14 @@ public class PackageParser { } public void setApplicationVolumeUuid(String volumeUuid) { + final UUID storageUuid = StorageManager.convert(volumeUuid); this.applicationInfo.volumeUuid = volumeUuid; + this.applicationInfo.storageUuid = storageUuid; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.volumeUuid = volumeUuid; + childPackages.get(i).applicationInfo.storageUuid = storageUuid; } } } diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index d79deb2b8459..0ad4874d41fe 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -16,11 +16,14 @@ package android.content.pm; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; @@ -31,28 +34,37 @@ import java.util.List; * static - updatable non backwards-compatible emulating static linking. */ public final class SharedLibraryInfo implements Parcelable { + + /** @hide */ + @IntDef( + flag = true, + value = { + TYPE_BUILTIN, + TYPE_DYNAMIC, + TYPE_STATIC, + }) + @Retention(RetentionPolicy.SOURCE) + @interface Type{} + /** * Shared library type: this library is a part of the OS * and cannot be updated or uninstalled. - * @hide */ - public static final int TYPE_BUILTIN = 0x1<<0; + public static final int TYPE_BUILTIN = 0; /** * Shared library type: this library is backwards-compatible, can * be updated, and updates can be uninstalled. Clients link against * the latest version of the library. - * @hide */ - public static final int TYPE_DYNAMIC = 0x1<<1; + public static final int TYPE_DYNAMIC = 1; /** * Shared library type: this library is <strong>not</strong> backwards * -compatible, can be updated and updates can be uninstalled. Clients * link against a specific version of the library. - * @hide */ - public static final int TYPE_STATIC = 0x1<<2; + public static final int TYPE_STATIC = 2; /** * Constant for referring to an undefined version. @@ -60,8 +72,10 @@ public final class SharedLibraryInfo implements Parcelable { public static final int VERSION_UNDEFINED = -1; private final String mName; + + // TODO: Make long when we change the paltform to use longs private final int mVersion; - private final int mType; + private final @Type int mType; private final VersionedPackage mDeclaringPackage; private final List<VersionedPackage> mDependentPackages; @@ -90,13 +104,18 @@ public final class SharedLibraryInfo implements Parcelable { parcel.readParcelable(null), parcel.readArrayList(null)); } - /** @hide */ - public int getType() { + /** + * Gets the type of this library. + * + * @return The library type. + */ + public @Type int getType() { return mType; } /** - * Gets the library name. + * Gets the library name an app defines in its manifest + * to depend on the library. * * @return The name. */ @@ -105,40 +124,36 @@ public final class SharedLibraryInfo implements Parcelable { } /** - * Gets the version of the library. For {@link #isStatic()} static} libraries - * this is the declared version and for {@link #isDynamic()} dynamic} and - * {@link #isBuiltin()} builtin} it is {@link #VERSION_UNDEFINED} as these + * Gets the version of the library. For {@link #TYPE_STATIC static} libraries + * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and + * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these * are not versioned. * * @return The version. */ - public @IntRange(from = -1) int getVersion() { + public @IntRange(from = -1) long getVersion() { return mVersion; } /** - * @return whether this library is builtin which means that it - * is a part of the OS and cannot be updated or uninstalled. + * @hide + * @removed */ public boolean isBuiltin() { return mType == TYPE_BUILTIN; } /** - * @return whether this library is dynamic which means that it - * is backwards-compatible, can be updated, and updates can be - * uninstalled. Clients link against the latest version of the - * library. + * @hide + * @removed */ public boolean isDynamic() { return mType == TYPE_DYNAMIC; } /** - * @return whether this library is dynamic which means that it - * is <strong>not</strong> backwards-compatible, can be updated - * and updates can be uninstalled. Clients link against a specific - * version of the library. + * @hide + * @removed */ public boolean isStatic() { return mType == TYPE_STATIC; diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f48afb55c58a..88bb1a4ce3b9 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -56,12 +56,13 @@ public class TypedArray { // Reset the assets, which may have changed due to configuration changes // or further resource loading. attrs.mAssets = res.getAssets(); + attrs.mMetrics = res.getDisplayMetrics(); attrs.resize(len); return attrs; } private final Resources mResources; - private final DisplayMetrics mMetrics; + private DisplayMetrics mMetrics; private AssetManager mAssets; private boolean mRecycled; diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index a895f820789c..f02e4849e7da 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -664,7 +664,7 @@ public final class Sensor { * @see #TYPE_LOW_LATENCY_OFFBODY_DETECT */ public static final String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = - "android.sensor.low_latency_offbody"; + "android.sensor.low_latency_offbody_detect"; /** * A constant describing an uncalibrated accelerometer sensor. diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index e8e989f20ef7..f61032ef8250 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -290,7 +290,8 @@ public final class CameraManager { cameraId, callback, handler, - characteristics); + characteristics, + mContext.getApplicationInfo().targetSdkVersion); ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index e75b3758a31e..ab87f15d8794 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -36,6 +36,7 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SubmitInfo; import android.hardware.camera2.utils.SurfaceUtils; import android.hardware.ICameraService; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -118,6 +119,8 @@ public class CameraDeviceImpl extends CameraDevice private CameraCaptureSessionCore mCurrentSession; private int mNextSessionId = 0; + private final int mAppTargetSdkVersion; + // Runnables for all state transitions, except error, which needs the // error code argument @@ -234,7 +237,7 @@ public class CameraDeviceImpl extends CameraDevice }; public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, - CameraCharacteristics characteristics) { + CameraCharacteristics characteristics, int appTargetSdkVersion) { if (cameraId == null || callback == null || handler == null || characteristics == null) { throw new IllegalArgumentException("Null argument given"); } @@ -242,6 +245,7 @@ public class CameraDeviceImpl extends CameraDevice mDeviceCallback = callback; mDeviceHandler = handler; mCharacteristics = characteristics; + mAppTargetSdkVersion = appTargetSdkVersion; final int MAX_TAG_LEN = 23; String tag = String.format("CameraDevice-JV-%s", mCameraId); @@ -671,6 +675,16 @@ public class CameraDeviceImpl extends CameraDevice } } + private void overrideEnableZsl(CameraMetadataNative request, boolean newValue) { + Boolean enableZsl = request.get(CaptureRequest.CONTROL_ENABLE_ZSL); + if (enableZsl == null) { + // If enableZsl is not available, don't override. + return; + } + + request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue); + } + @Override public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException { @@ -681,6 +695,13 @@ public class CameraDeviceImpl extends CameraDevice templatedRequest = mRemoteDevice.createDefaultRequest(templateType); + // If app target SDK is older than O, or it's not a still capture template, enableZsl + // must be false in the default request. + if (mAppTargetSdkVersion < Build.VERSION_CODES.O || + templateType != TEMPLATE_STILL_CAPTURE) { + overrideEnableZsl(templatedRequest, false); + } + CaptureRequest.Builder builder = new CaptureRequest.Builder( templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java index 04482210c088..3c6baa76558a 100644 --- a/core/java/android/metrics/LogMaker.java +++ b/core/java/android/metrics/LogMaker.java @@ -54,7 +54,11 @@ public class LogMaker { /* Deserialize from the eventlog */ public LogMaker(Object[] items) { - deserialize(items); + if (items != null) { + deserialize(items); + } else { + setCategory(MetricsEvent.VIEW_UNKNOWN); + } } /** @param category to replace the existing setting. */ @@ -94,6 +98,16 @@ public class LogMaker { } /** + * Set event latency. + * + * @hide // TODO Expose in the future? Too late for O. + */ + public LogMaker setLatency(long milliseconds) { + entries.put(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, milliseconds); + return this; + } + + /** * This will be set by the system when the log is persisted. * Client-supplied values will be ignored. * @@ -363,13 +377,13 @@ public class LogMaker { */ public void deserialize(Object[] items) { int i = 0; - while (i < items.length) { + while (items != null && i < items.length) { Object key = items[i++]; Object value = i < items.length ? items[i++] : null; if (key instanceof Integer) { entries.put((Integer) key, value); } else { - Log.i(TAG, "Invalid key " + key.toString()); + Log.i(TAG, "Invalid key " + (key == null ? "null" : key.toString())); } } } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index f8702e2e00e7..375b7eeb013a 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -245,6 +245,7 @@ public final class IpSecManager { * * @param socket a stream socket * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. + * @hide */ public void applyTransportModeTransform(Socket socket, IpSecTransform transform) throws IOException { @@ -262,6 +263,7 @@ public final class IpSecManager { * * @param socket a datagram socket * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. + * @hide */ public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform) throws IOException { @@ -284,7 +286,7 @@ public final class IpSecManager { * address associated with that transform will throw an IOException. In addition, if the * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to * send() or receive() until the transform is removed from the socket by calling {@link - * #removeTransportModeTransform(Socket, IpSecTransform)}; + * #removeTransportModeTransform(FileDescriptor, IpSecTransform)}; * * @param socket a socket file descriptor * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform. @@ -316,8 +318,10 @@ public final class IpSecManager { * * @param socket a socket that previously had a transform applied to it. * @param transform the IPsec Transform that was previously applied to the given socket + * @hide */ - public void removeTransportModeTransform(Socket socket, IpSecTransform transform) { + public void removeTransportModeTransform(Socket socket, IpSecTransform transform) + throws IOException { removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform); } @@ -330,8 +334,10 @@ public final class IpSecManager { * * @param socket a socket that previously had a transform applied to it. * @param transform the IPsec Transform that was previously applied to the given socket + * @hide */ - public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) { + public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) + throws IOException { removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform); } @@ -345,7 +351,8 @@ public final class IpSecManager { * @param socket a socket file descriptor that previously had a transform applied to it. * @param transform the IPsec Transform that was previously applied to the given socket */ - public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform) { + public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform) + throws IOException { removeTransportModeTransform(new ParcelFileDescriptor(socket), transform); } @@ -419,7 +426,7 @@ public final class IpSecManager { * * @param fd a file descriptor previously returned as a UDP Encapsulation socket. */ - public void close() { + public void close() throws IOException { // TODO: Go close the socket mCloseGuard.close(); } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index afca0b0e0bd3..bf7207ca5bbb 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -19,6 +19,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.BitUtils; import java.util.Objects; @@ -230,7 +231,8 @@ public final class NetworkCapabilities implements Parcelable { * Capabilities that suggest that a network is restricted. * {@see #maybeMarkCapabilitiesRestricted}. */ - private static final long RESTRICTED_CAPABILITIES = + @VisibleForTesting + /* package */ static final long RESTRICTED_CAPABILITIES = (1 << NET_CAPABILITY_CBS) | (1 << NET_CAPABILITY_DUN) | (1 << NET_CAPABILITY_EIMS) | @@ -241,6 +243,17 @@ public final class NetworkCapabilities implements Parcelable { (1 << NET_CAPABILITY_XCAP); /** + * Capabilities that suggest that a network is unrestricted. + * {@see #maybeMarkCapabilitiesRestricted}. + */ + @VisibleForTesting + /* package */ static final long UNRESTRICTED_CAPABILITIES = + (1 << NET_CAPABILITY_INTERNET) | + (1 << NET_CAPABILITY_MMS) | + (1 << NET_CAPABILITY_SUPL) | + (1 << NET_CAPABILITY_WIFI_P2P); + + /** * Adds the given capability to this {@code NetworkCapability} instance. * Multiple capabilities may be applied sequentially. Note that when searching * for a network to satisfy a request, all capabilities requested must be satisfied. @@ -353,12 +366,16 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void maybeMarkCapabilitiesRestricted() { - // If all the capabilities are typically provided by restricted networks, conclude that this - // network is restricted. - if ((mNetworkCapabilities & ~(DEFAULT_CAPABILITIES | RESTRICTED_CAPABILITIES)) == 0 && - // Must have at least some restricted capabilities, otherwise a request for an - // internet-less network will get marked restricted. - (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0) { + // Verify there aren't any unrestricted capabilities. If there are we say + // the whole thing is unrestricted. + final boolean hasUnrestrictedCapabilities = + ((mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0); + + // Must have at least some restricted capabilities. + final boolean hasRestrictedCapabilities = + ((mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0); + + if (hasRestrictedCapabilities && !hasUnrestrictedCapabilities) { removeCapability(NET_CAPABILITY_NOT_RESTRICTED); } } diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 33d12a3a3fb8..1dde3cad23f3 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -45,7 +45,7 @@ import com.android.internal.util.Protocol; * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt * * <p> The API is asynchronous and responses to requests from an application are on listener - * callbacks on a seperate thread. + * callbacks on a seperate internal thread. * * <p> There are three main operations the API supports - registration, discovery and resolution. * <pre> @@ -119,8 +119,8 @@ import com.android.internal.util.Protocol; * {@see NsdServiceInfo} */ public final class NsdManager { - private static final String TAG = "NsdManager"; - INsdManager mService; + private static final String TAG = NsdManager.class.getSimpleName(); + private static final boolean DBG = false; /** * Broadcast intent action to indicate whether network service discovery is @@ -130,8 +130,7 @@ public final class NsdManager { * @see #EXTRA_NSD_STATE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_NSD_STATE_CHANGED = - "android.net.nsd.STATE_CHANGED"; + public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; /** * The lookup key for an int that indicates whether network service discovery is enabled @@ -208,13 +207,47 @@ public final class NsdManager { /** Dns based service discovery protocol */ public static final int PROTOCOL_DNS_SD = 0x0001; - private Context mContext; + private static final SparseArray<String> EVENT_NAMES = new SparseArray<>(); + static { + EVENT_NAMES.put(DISCOVER_SERVICES, "DISCOVER_SERVICES"); + EVENT_NAMES.put(DISCOVER_SERVICES_STARTED, "DISCOVER_SERVICES_STARTED"); + EVENT_NAMES.put(DISCOVER_SERVICES_FAILED, "DISCOVER_SERVICES_FAILED"); + EVENT_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND"); + EVENT_NAMES.put(SERVICE_LOST, "SERVICE_LOST"); + EVENT_NAMES.put(STOP_DISCOVERY, "STOP_DISCOVERY"); + EVENT_NAMES.put(STOP_DISCOVERY_FAILED, "STOP_DISCOVERY_FAILED"); + EVENT_NAMES.put(STOP_DISCOVERY_SUCCEEDED, "STOP_DISCOVERY_SUCCEEDED"); + EVENT_NAMES.put(REGISTER_SERVICE, "REGISTER_SERVICE"); + EVENT_NAMES.put(REGISTER_SERVICE_FAILED, "REGISTER_SERVICE_FAILED"); + EVENT_NAMES.put(REGISTER_SERVICE_SUCCEEDED, "REGISTER_SERVICE_SUCCEEDED"); + EVENT_NAMES.put(UNREGISTER_SERVICE, "UNREGISTER_SERVICE"); + EVENT_NAMES.put(UNREGISTER_SERVICE_FAILED, "UNREGISTER_SERVICE_FAILED"); + EVENT_NAMES.put(UNREGISTER_SERVICE_SUCCEEDED, "UNREGISTER_SERVICE_SUCCEEDED"); + EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); + EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); + EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); + EVENT_NAMES.put(ENABLE, "ENABLE"); + EVENT_NAMES.put(DISABLE, "DISABLE"); + EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT"); + } + + /** @hide */ + public static String nameOf(int event) { + String name = EVENT_NAMES.get(event); + if (name == null) { + return Integer.toString(event); + } + return name; + } + + private final INsdManager mService; + private final Context mContext; private static final int INVALID_LISTENER_KEY = 0; private static final int BUSY_LISTENER_KEY = -1; private int mListenerKey = 1; private final SparseArray mListenerMap = new SparseArray(); - private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<NsdServiceInfo>(); + private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); private final Object mMapLock = new Object(); private final AsyncChannel mAsyncChannel = new AsyncChannel(); @@ -300,6 +333,7 @@ public final class NsdManager { @Override public void handleMessage(Message message) { + if (DBG) Log.d(TAG, "received " + nameOf(message.what)); switch (message.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); @@ -377,7 +411,6 @@ public final class NsdManager { // if the listener is already in the map, reject it. Otherwise, add it and // return its key. - private int putListener(Object listener, NsdServiceInfo s) { if (listener == null) return INVALID_LISTENER_KEY; int key; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 832031e16ea6..235f24cd3602 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1365,8 +1365,6 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_WAKEUP_AP = 0x0013; // Event for reporting that a specific partial wake lock has been held for a long duration. public static final int EVENT_LONG_WAKE_LOCK = 0x0014; - // Event reporting the new estimated (learned) capacity of the battery in mAh. - public static final int EVENT_ESTIMATED_BATTERY_CAP = 0x0015; // Number of event types. public static final int EVENT_COUNT = 0x0016; @@ -2501,6 +2499,16 @@ public abstract class BatteryStats implements Parcelable { public abstract int getEstimatedBatteryCapacity(); /** + * @return The minimum learned battery capacity in uAh. + */ + public abstract int getMinLearnedBatteryCapacity(); + + /** + * @return The maximum learned battery capacity in uAh. + */ + public abstract int getMaxLearnedBatteryCapacity() ; + + /** * Return the array of discharge step durations. */ public abstract LevelStepTracker getDischargeLevelStepTracker(); @@ -2990,13 +2998,14 @@ public abstract class BatteryStats implements Parcelable { final String category = STAT_NAMES[which]; // Dump "battery" stat - dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, + dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, which == STATS_SINCE_CHARGED ? getStartCount() : "N/A", whichBatteryRealtime / 1000, whichBatteryUptime / 1000, totalRealtime / 1000, totalUptime / 1000, getStartClockTime(), whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000, - getEstimatedBatteryCapacity()); + getEstimatedBatteryCapacity(), + getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity()); // Calculate wakelock times across all uids. @@ -3583,6 +3592,25 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + final int minLearnedBatteryCapacity = getMinLearnedBatteryCapacity(); + if (minLearnedBatteryCapacity > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Min learned battery capacity: "); + sb.append(BatteryStatsHelper.makemAh(minLearnedBatteryCapacity / 1000)); + sb.append(" mAh"); + pw.println(sb.toString()); + } + final int maxLearnedBatteryCapacity = getMaxLearnedBatteryCapacity(); + if (maxLearnedBatteryCapacity > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Max learned battery capacity: "); + sb.append(BatteryStatsHelper.makemAh(maxLearnedBatteryCapacity / 1000)); + sb.append(" mAh"); + pw.println(sb.toString()); + } + sb.setLength(0); sb.append(prefix); sb.append(" Time on battery: "); diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 9b5ff29d592e..167c46d6ded0 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -25,6 +25,7 @@ import android.util.SparseArray; import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * A mapping from String keys to various {@link Parcelable} values. @@ -476,6 +477,18 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Inserts a UUID value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a UUID object, or null + */ + public void putUuid(@Nullable String key, @Nullable UUID value) { + unparcel(); + mMap.put(key, value); + } + + /** * Inserts an array of Parcelable values into the mapping of this Bundle, * replacing any existing value for the given key. Either key or value may * be null. @@ -858,6 +871,26 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { * value is explicitly associated with the key. * * @param key a String, or null + * @return a UUID value, or null + */ + @Nullable + public UUID getUuid(@Nullable String key) { + unparcel(); + final Object o = mMap.get(key); + try { + return (UUID) o; + } catch (ClassCastException e) { + typeWarning(key, o, "UUID", e); + return null; + } + } + + /** + * Returns the value associated with the given key, or null if + * no mapping of the desired type exists for the given key or a null + * value is explicitly associated with the key. + * + * @param key a String, or null * @return a Bundle value, or null */ @Nullable diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index 94fd5b074b82..4ba1144742db 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -209,10 +209,11 @@ public class HwParcel { public native final IHwBinder readStrongBinder(); // Handle is stored as part of the blob. - public native final HwBlob readBuffer(); + public native final HwBlob readBuffer(long expectedSize); public native final HwBlob readEmbeddedBuffer( - long parentHandle, long offset, boolean nullable); + long expectedSize, long parentHandle, long offset, + boolean nullable); public native final void writeBuffer(HwBlob blob); diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index c3836a38c822..c1647c74f363 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -16,8 +16,6 @@ package android.os; -import android.annotation.IntegerRes; -import android.annotation.NonNull; import android.annotation.Nullable; import android.text.TextUtils; import android.util.ArrayMap; @@ -29,6 +27,9 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import dalvik.annotation.optimization.FastNative; +import dalvik.system.VMRuntime; + import libcore.util.SneakyThrow; import java.io.ByteArrayInputStream; @@ -49,9 +50,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; - -import dalvik.annotation.optimization.FastNative; -import dalvik.system.VMRuntime; +import java.util.UUID; /** * Container for a message (data and object references) that can @@ -243,6 +242,7 @@ public final class Parcel { private static final int VAL_SIZE = 26; private static final int VAL_SIZEF = 27; private static final int VAL_DOUBLEARRAY = 28; + private static final int VAL_UUID = 29; // The initial int32 in a Binder call's reply Parcel header: // Keep these in sync with libbinder's binder/Status.h. @@ -831,6 +831,15 @@ public final class Parcel { } /** + * Flatten a UUID into the parcel at the current dataPosition(), + * growing dataCapacity() if needed. + */ + public final void writeUuid(UUID val) { + writeLong(val.getMostSignificantBits()); + writeLong(val.getLeastSignificantBits()); + } + + /** * Flatten a List into the parcel at the current dataPosition(), growing * dataCapacity() if needed. The List values are written using * {@link #writeValue} and must follow the specification there. @@ -1678,6 +1687,9 @@ public final class Parcel { } else if (v instanceof double[]) { writeInt(VAL_DOUBLEARRAY); writeDoubleArray((double[]) v); + } else if (v instanceof UUID) { + writeInt(VAL_UUID); + writeUuid((UUID) v); } else { Class<?> clazz = v.getClass(); if (clazz.isArray() && clazz.getComponentType() == Object.class) { @@ -2182,6 +2194,13 @@ public final class Parcel { } /** + * Read a UUID from the parcel at the current dataPosition(). + */ + public final UUID readUuid() { + return new UUID(readLong(), readLong()); + } + + /** * Read and return a byte[] object from the parcel. */ public final byte[] createByteArray() { @@ -2731,6 +2750,9 @@ public final class Parcel { case VAL_DOUBLEARRAY: return createDoubleArray(); + case VAL_UUID: + return readUuid(); + default: int off = dataPosition() - 4; throw new RuntimeException( diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 5f66abd58d8b..447f2805d075 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -16,6 +16,8 @@ package android.os; +import static java.nio.charset.StandardCharsets.UTF_8; + import android.annotation.SystemApi; import android.content.BroadcastReceiver; import android.content.Context; @@ -24,9 +26,12 @@ import android.os.UserManager; import android.text.TextUtils; import android.util.Log; +import libcore.io.Streams; + import java.io.ByteArrayInputStream; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; @@ -39,6 +44,7 @@ import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; @@ -46,6 +52,7 @@ import java.util.List; import java.util.Locale; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; import com.android.internal.logging.MetricsLogger; @@ -317,6 +324,70 @@ public class RecoverySystem { } finally { raf.close(); } + + // Additionally verify the package compatibility. + if (!readAndVerifyPackageCompatibilityEntry(packageFile)) { + throw new SignatureException("package compatibility verification failed"); + } + } + + /** + * Verifies the compatibility entry from an {@link InputStream}. + * + * @return the verification result. + */ + private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException { + ArrayList<String> list = new ArrayList<>(); + ZipInputStream zis = new ZipInputStream(inputStream); + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + long entrySize = entry.getSize(); + if (entrySize > Integer.MAX_VALUE || entrySize < 0) { + throw new IOException( + "invalid entry size (" + entrySize + ") in the compatibility file"); + } + byte[] bytes = new byte[(int) entrySize]; + Streams.readFully(zis, bytes); + list.add(new String(bytes, UTF_8)); + } + if (list.isEmpty()) { + throw new IOException("no entries found in the compatibility file"); + } + return (VintfObject.verify(list.toArray(new String[list.size()])) == 0); + } + + /** + * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is + * a zip file (inside the OTA package zip). + * + * @return {@code true} if the entry doesn't exist or verification passes. + */ + private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile) + throws IOException { + try (ZipFile zip = new ZipFile(packageFile)) { + ZipEntry entry = zip.getEntry("compatibility.zip"); + if (entry == null) { + return true; + } + InputStream inputStream = zip.getInputStream(entry); + return verifyPackageCompatibility(inputStream); + } + } + + /** + * Verifies the package compatibility info against the current system. + * + * @param compatibilityFile the {@link File} that contains the package compatibility info. + * @throws IOException if there were any errors reading the compatibility file. + * @return the compatibility verification result. + * + * {@hide} + */ + @SystemApi + public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException { + try (InputStream inputStream = new FileInputStream(compatibilityFile)) { + return verifyPackageCompatibility(inputStream); + } } /** diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index b5af766fd159..1c15004c894d 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -66,6 +66,7 @@ import com.android.internal.util.Preconditions; import java.io.File; import java.io.FileDescriptor; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -77,6 +78,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.UUID; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; @@ -117,19 +119,66 @@ public class StorageManager { public static final String UUID_PRIVATE_INTERNAL = null; /** {@hide} */ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; + /** {@hide} */ + public static final String UUID_SYSTEM = "system"; + // NOTE: UUID constants below are namespaced + // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default + // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical + // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system /** - * Activity Action: Allows the user to manage their storage. This activity provides the ability - * to free up space on the device by deleting data such as apps. + * UUID representing the default internal storage of this device which + * provides {@link Environment#getDataDirectory()}. * <p> - * Input: Nothing. + * This value is constant across all devices and it will never change, and + * thus it cannot be used to uniquely identify a particular physical device. + * + * @see #getUuidForPath(File) + */ + public static final UUID UUID_DEFAULT = UUID + .fromString("41217664-9172-527a-b3d5-edabb50a7d69"); + + /** {@hide} */ + public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID + .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd"); + + /** {@hide} */ + public static final UUID UUID_SYSTEM_ = UUID + .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0"); + + /** + * Activity Action: Allows the user to manage their storage. This activity + * provides the ability to free up space on the device by deleting data such + * as apps. * <p> - * Output: Nothing. + * If the sending application has a specific storage device or allocation + * size in mind, they can optionally define {@link #EXTRA_UUID} or + * {@link #EXTRA_REQUESTED_BYTES}, respectively. */ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_MANAGE_STORAGE - = "android.os.storage.action.MANAGE_STORAGE"; + public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + + /** + * Extra {@link UUID} used to indicate the storage volume where an + * application is interested in allocating or managing disk space. + * + * @see #ACTION_MANAGE_STORAGE + * @see #UUID_DEFAULT + * @see #getUuidForPath(File) + */ + public static final String EXTRA_UUID = "android.os.storage.extra.UUID"; + + /** + * Extra used to indicate the total size (in bytes) that an application is + * interested in allocating. + * <p> + * When defined, the management UI will help guide the user to free up + * enough disk space to reach this requested value. + * + * @see #ACTION_MANAGE_STORAGE + */ + public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; /** {@hide} */ public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; @@ -668,34 +717,44 @@ public class StorageManager { } } - /** {@hide} */ - public @Nullable String findUuidForPath(File path) { + /** + * Return a UUID identifying the storage volume that hosts the given + * filesystem path. + * <p> + * If this path is hosted by the default internal storage of the device at + * {@link Environment#getDataDirectory()}, the returned value will be + * {@link #UUID_DEFAULT}. + * + * @throws IOException when the storage device at the given path isn't + * present, or when it doesn't have a valid UUID. + */ + public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException { Preconditions.checkNotNull(path); final String pathString = path.getAbsolutePath(); if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { - return StorageManager.UUID_PRIVATE_INTERNAL; + return UUID_DEFAULT; } try { for (VolumeInfo vol : mStorageManager.getVolumes(0)) { if (vol.path != null && FileUtils.contains(vol.path, pathString)) { // TODO: verify that emulated adopted devices have UUID of // underlying volume - return vol.fsUuid; + return convert(vol.fsUuid); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - throw new IllegalStateException("Failed to find a storage device for " + path); + throw new FileNotFoundException("Failed to find a storage device for " + path); } /** {@hide} */ - public @Nullable File findPathForUuid(String volumeUuid) { + public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException { final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid); if (vol != null) { return vol.getPath(); } - throw new IllegalStateException("Failed to find a storage device for " + volumeUuid); + throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid); } /** {@hide} */ @@ -1451,7 +1510,7 @@ public class StorageManager { /** * Return quota size in bytes for all cached data belonging to the calling - * app on the filesystem that hosts the given path. + * app on the given storage volume. * <p> * If your app goes above this quota, your cached files will be some of the * first to be deleted when additional disk space is needed. Conversely, if @@ -1459,28 +1518,42 @@ public class StorageManager { * last to be deleted when additional disk space is needed. * <p> * This quota will change over time depending on how frequently the user - * interacts with your app, and depending on how much disk space is used. + * interacts with your app, and depending on how much system-wide disk space + * is used. * <p class="note"> * Note: if your app uses the {@code android:sharedUserId} manifest feature, * then cached data for all packages in your shared UID is tracked together * as a single unit. * </p> * - * @see #getCacheSizeBytes(File) + * @param storageUuid the UUID of the storage volume that you're interested + * in. The UUID for a specific path can be obtained using + * {@link #getUuidForPath(File)}. + * @throws IOException when the storage device isn't present, or when it + * doesn't support cache quotas. + * @see #getCacheSizeBytes(UUID) */ - public long getCacheQuotaBytes(File path) { + public long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException { try { - final String volumeUuid = findUuidForPath(path); final ApplicationInfo app = mContext.getApplicationInfo(); - return mStorageManager.getCacheQuotaBytes(volumeUuid, app.uid); + return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** @removed */ + @Deprecated + public long getCacheQuotaBytes(@NonNull File path) throws IOException { + return getCacheQuotaBytes(getUuidForPath(path)); + } + /** * Return total size in bytes of all cached data belonging to the calling - * app on the filesystem that hosts the given path. + * app on the given storage volume. * <p> * Cached data tracked by this method always includes * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and @@ -1493,13 +1566,20 @@ public class StorageManager { * as a single unit. * </p> * - * @see #getCacheQuotaBytes() + * @param storageUuid the UUID of the storage volume that you're interested + * in. The UUID for a specific path can be obtained using + * {@link #getUuidForPath(File)}. + * @throws IOException when the storage device isn't present, or when it + * doesn't support cache quotas. + * @see #getCacheQuotaBytes(UUID) */ - public long getCacheSizeBytes(File path) { + public long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException { try { - final String volumeUuid = findUuidForPath(path); final ApplicationInfo app = mContext.getApplicationInfo(); - return mStorageManager.getCacheSizeBytes(volumeUuid, app.uid); + return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1507,25 +1587,31 @@ public class StorageManager { /** @removed */ @Deprecated - public long getCacheQuotaBytes() { + public long getCacheSizeBytes(@NonNull File path) throws IOException { + return getCacheSizeBytes(getUuidForPath(path)); + } + + /** @removed */ + @Deprecated + public long getCacheQuotaBytes() throws IOException { return getCacheQuotaBytes(mContext.getCacheDir()); } /** @removed */ @Deprecated - public long getCacheSizeBytes() { + public long getCacheSizeBytes() throws IOException { return getCacheSizeBytes(mContext.getCacheDir()); } /** @removed */ @Deprecated - public long getExternalCacheQuotaBytes() { + public long getExternalCacheQuotaBytes() throws IOException { return getCacheQuotaBytes(mContext.getExternalCacheDir()); } /** @removed */ @Deprecated - public long getExternalCacheSizeBytes() { + public long getExternalCacheSizeBytes() throws IOException { return getCacheSizeBytes(mContext.getExternalCacheDir()); } @@ -1542,8 +1628,8 @@ public class StorageManager { * this flag to take effect. * </p> * - * @see #getAllocatableBytes(File, int) - * @see #allocateBytes(File, long, int) + * @see #getAllocatableBytes(UUID, int) + * @see #allocateBytes(UUID, long, int) * @see #allocateBytes(FileDescriptor, long, int) */ @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) @@ -1558,32 +1644,43 @@ public class StorageManager { /** * Return the maximum number of new bytes that your app can allocate for - * itself using {@link #allocateBytes(File, long, int)} at the given path. - * This value is typically larger than {@link File#getUsableSpace()}, since - * the system may be willing to delete cached files to satisfy an allocation - * request. + * itself on the given storage volume. This value is typically larger than + * {@link File#getUsableSpace()}, since the system may be willing to delete + * cached files to satisfy an allocation request. You can then allocate + * space for yourself using {@link #allocateBytes(UUID, long, int)} or + * {@link #allocateBytes(FileDescriptor, long, int)}. * <p> * This method is best used as a pre-flight check, such as deciding if there * is enough space to store an entire music album before you allocate space * for each audio file in the album. Attempts to allocate disk space beyond * the returned value will fail. + * <p> + * If the returned value is not large enough for the data you'd like to + * store, you can launch {@link #ACTION_MANAGE_STORAGE} with the + * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help + * involve the user in freeing up disk space. * <p class="note"> * Note: if your app uses the {@code android:sharedUserId} manifest feature, * then allocatable space for all packages in your shared UID is tracked * together as a single unit. * </p> * - * @param path the path where you're considering allocating disk space, - * since allocatable space can vary widely depending on the - * underlying storage device. + * @param storageUuid the UUID of the storage volume where you're + * considering allocating disk space, since allocatable space can + * vary widely depending on the underlying storage device. The + * UUID for a specific path can be obtained using + * {@link #getUuidForPath(File)}. * @param flags to apply to the request. * @return the maximum number of new bytes that the calling app can allocate - * using {@link #allocateBytes(File, long, int)}. + * using {@link #allocateBytes(UUID, long, int)} or + * {@link #allocateBytes(FileDescriptor, long, int)}. + * @throws IOException when the storage device isn't present, or when it + * doesn't support allocating space. */ - public long getAllocatableBytes(File path, @AllocateFlags int flags) throws IOException { + public long getAllocatableBytes(@NonNull UUID storageUuid, @AllocateFlags int flags) + throws IOException { try { - final String volumeUuid = findUuidForPath(path); - return mStorageManager.getAllocatableBytes(volumeUuid, flags); + return mStorageManager.getAllocatableBytes(convert(storageUuid), flags); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); throw new RuntimeException(e); @@ -1592,28 +1689,40 @@ public class StorageManager { } } + /** @removed */ + @Deprecated + public long getAllocatableBytes(@NonNull File path, @AllocateFlags int flags) + throws IOException { + return getAllocatableBytes(getUuidForPath(path), flags); + } + /** - * Allocate the requested number of bytes for your application to use at the - * given path. This will cause the system to delete any cached files - * necessary to satisfy your request. + * Allocate the requested number of bytes for your application to use on the + * given storage volume. This will cause the system to delete any cached + * files necessary to satisfy your request. * <p> * Attempts to allocate disk space beyond the value returned by - * {@link #getAllocatableBytes(File, int)} will fail. + * {@link #getAllocatableBytes(UUID, int)} will fail. * <p> * Since multiple apps can be running simultaneously, this method may be * subject to race conditions. If possible, consider using * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee * that bytes are allocated to an opened file. * - * @param path the path where you'd like to allocate disk space. + * @param storageUuid the UUID of the storage volume where you'd like to + * allocate disk space. The UUID for a specific path can be + * obtained using {@link #getUuidForPath(File)}. * @param bytes the number of bytes to allocate. * @param flags to apply to the request. - * @see #getAllocatableBytes(File, int) + * @throws IOException when the storage device isn't present, or when it + * doesn't support allocating space, or if the device had + * trouble allocating the requested space. + * @see #getAllocatableBytes(UUID, int) */ - public void allocateBytes(File path, long bytes, @AllocateFlags int flags) throws IOException { + public void allocateBytes(@NonNull UUID storageUuid, long bytes, @AllocateFlags int flags) + throws IOException { try { - final String volumeUuid = findUuidForPath(path); - mStorageManager.allocateBytes(volumeUuid, bytes, flags); + mStorageManager.allocateBytes(convert(storageUuid), bytes, flags); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); } catch (RemoteException e) { @@ -1621,13 +1730,20 @@ public class StorageManager { } } + /** @removed */ + @Deprecated + public void allocateBytes(@NonNull File path, long bytes, @AllocateFlags int flags) + throws IOException { + allocateBytes(getUuidForPath(path), bytes, flags); + } + /** * Allocate the requested number of bytes for your application to use in the * given open file. This will cause the system to delete any cached files * necessary to satisfy your request. * <p> * Attempts to allocate disk space beyond the value returned by - * {@link #getAllocatableBytes(File, int)} will fail. + * {@link #getAllocatableBytes(UUID, int)} will fail. * <p> * This method guarantees that bytes have been allocated to the opened file, * otherwise it will throw if fast allocation is not possible. Fast @@ -1636,9 +1752,15 @@ public class StorageManager { * * @param fd the open file that you'd like to allocate disk space for. * @param bytes the number of bytes to allocate. This is the desired final - * size of the open file. + * size of the open file. If the open file is smaller than this + * requested size, it will be extended without modifying any + * existing contents. If the open file is larger than this + * requested size, it will be truncated. * @param flags to apply to the request. - * @see #getAllocatableBytes(File, int) + * @throws IOException when the storage device isn't present, or when it + * doesn't support allocating space, or if the device had + * trouble allocating the requested space. + * @see #getAllocatableBytes(UUID, int) * @see Environment#isExternalStorageEmulated(File) */ public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags) @@ -1777,6 +1899,32 @@ public class StorageManager { return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE); } + /** {@hide} */ + public static UUID convert(String uuid) { + if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) { + return UUID_DEFAULT; + } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) { + return UUID_PRIMARY_PHYSICAL_; + } else if (Objects.equals(uuid, UUID_SYSTEM)) { + return UUID_SYSTEM_; + } else { + return UUID.fromString(uuid); + } + } + + /** {@hide} */ + public static String convert(UUID storageUuid) { + if (UUID_DEFAULT.equals(storageUuid)) { + return UUID_PRIVATE_INTERNAL; + } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) { + return UUID_PRIMARY_PHYSICAL; + } else if (UUID_SYSTEM_.equals(storageUuid)) { + return UUID_SYSTEM; + } else { + return storageUuid.toString(); + } + } + private final Object mFuseAppLoopLock = new Object(); @GuardedBy("mFuseAppLoopLock") diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index d3adce73582e..7496cb28f046 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -135,6 +135,7 @@ public class Preference implements Comparable<Preference> { private boolean mDependencyMet = true; private boolean mParentDependencyMet = true; private boolean mRecycleEnabled = true; + private boolean mHasSingleLineTitleAttr; private boolean mSingleLineTitle = true; private boolean mIconSpaceReserved; @@ -303,6 +304,7 @@ public class Preference implements Comparable<Preference> { case com.android.internal.R.styleable.Preference_singleLineTitle: mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle); + mHasSingleLineTitleAttr = true; break; case com.android.internal.R.styleable.Preference_iconSpaceReserved: @@ -609,7 +611,9 @@ public class Preference implements Comparable<Preference> { if (!TextUtils.isEmpty(title)) { titleView.setText(title); titleView.setVisibility(View.VISIBLE); - titleView.setSingleLine(mSingleLineTitle); + if (mHasSingleLineTitleAttr) { + titleView.setSingleLine(mSingleLineTitle); + } } else { titleView.setVisibility(View.GONE); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 539559d19c82..22de27f81034 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5082,6 +5082,10 @@ public final class Settings { * (available on certain devices running Android 4.2 or higher), each user appears as a * completely separate device, so the {@code ANDROID_ID} value is unique to each * user.</p> + * + * <p class="note"><strong>Note:</strong> If the caller is an Instant App the id is scoped + * to the Instant App, it is generated when the Instant App is first installed and reset if + * the user clears the Instant App. */ public static final String ANDROID_ID = "android_id"; @@ -7077,6 +7081,7 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED); CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION); CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS); + CLONE_TO_MANAGED_PROFILE.add(AUTOFILL_SERVICE); CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD); CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES); CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS); @@ -8330,7 +8335,6 @@ public final class Settings { * enabled state. * @hide */ - @SystemApi public static final String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled"; diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 5e49b8f0553f..813c54f53db9 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -247,7 +247,7 @@ public abstract class AutofillService extends Service { * @param callback object used to notify the result of the request. */ public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) { - List<FillContext> contexts = request.getFillContexts(); + final List<FillContext> contexts = request.getFillContexts(); onSaveRequest(contexts.get(contexts.size() - 1).getStructure(), request.getClientState(), callback); } @@ -285,4 +285,22 @@ public abstract class AutofillService extends Service { // TODO(b/33197203): Remove when GCore has migrated off this API getSystemService(AutofillManager.class).disableOwnedAutofillServices(); } + + /** + * Returns the {@link FillEventHistory.Event events} since the last {@link FillResponse} was + * returned. + * + * <p>The history is not persisted over reboots. + * + * @return The history or {@code null} if there are not events. + */ + @Nullable public final FillEventHistory getFillEventHistory() { + AutofillManager afm = getSystemService(AutofillManager.class); + + if (afm == null) { + return null; + } else { + return afm.getFillEventHistory(); + } + } } diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index e77bd0d753ac..e04fae7bc2b5 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -24,6 +24,7 @@ import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.widget.RemoteViews; import com.android.internal.util.Preconditions; @@ -50,6 +51,7 @@ public final class Dataset implements Parcelable { private final ArrayList<RemoteViews> mFieldPresentations; private final RemoteViews mPresentation; private final IntentSender mAuthentication; + @Nullable String mId; private Dataset(Builder builder) { mFieldIds = builder.mFieldIds; @@ -57,6 +59,7 @@ public final class Dataset implements Parcelable { mFieldPresentations = builder.mFieldPresentations; mPresentation = builder.mPresentation; mAuthentication = builder.mAuthentication; + mId = builder.mId; } /** @hide */ @@ -89,7 +92,7 @@ public final class Dataset implements Parcelable { public String toString() { if (!DEBUG) return super.toString(); - return new StringBuilder("Dataset [") + return new StringBuilder("Dataset " + mId + " [") .append("fieldIds=").append(mFieldIds) .append(", fieldValues=").append(mFieldValues) .append(", fieldPresentations=") @@ -100,6 +103,17 @@ public final class Dataset implements Parcelable { } /** + * Gets the id of this dataset. + * + * @return The id of this dataset or {@code null} if not set + * + * @hide + */ + public String getId() { + return mId; + } + + /** * A builder for {@link Dataset} objects. You must to provide at least * one value for a field or set an authentication intent. */ @@ -110,6 +124,7 @@ public final class Dataset implements Parcelable { private RemoteViews mPresentation; private IntentSender mAuthentication; private boolean mDestroyed; + @Nullable private String mId; /** * Creates a new builder. @@ -173,6 +188,25 @@ public final class Dataset implements Parcelable { } /** + * Sets the id for the dataset. + * + * <p>The id of the last selected dataset can be read from + * {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear + * if a dataset was selected as {@link AutofillService#getFillEventHistory()} uses + * {@code null} to indicate that no dataset was selected. + * + * @param id id for this dataset or {@code null} to unset. + + * @return This builder. + */ + public @NonNull Builder setId(@Nullable String id) { + throwIfDestroyed(); + + mId = id; + return this; + } + + /** * Sets the value of a field. * * @param id id returned by {@link @@ -269,6 +303,7 @@ public final class Dataset implements Parcelable { parcel.writeTypedArrayList(mFieldValues, flags); parcel.writeParcelableList(mFieldPresentations, flags); parcel.writeParcelable(mAuthentication, flags); + parcel.writeString(mId); } public static final Creator<Dataset> CREATOR = new Creator<Dataset>() { @@ -295,6 +330,7 @@ public final class Dataset implements Parcelable { builder.setValueAndPresentation(id, value, fieldPresentation); } builder.setAuthentication(parcel.readParcelable(null)); + builder.setId(parcel.readString()); return builder.build(); } diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java index a009be805ce7..7774bdd4c410 100644 --- a/core/java/android/service/autofill/FillCallback.java +++ b/core/java/android/service/autofill/FillCallback.java @@ -18,7 +18,6 @@ package android.service.autofill; import android.annotation.Nullable; import android.app.Activity; -import android.os.Bundle; import android.os.RemoteException; /** @@ -38,8 +37,8 @@ public final class FillCallback { /** * Notifies the Android System that an - * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure, Bundle, - * int, android.os.CancellationSignal, FillCallback)} was successfully fulfilled by the service. + * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, + * FillCallback)} was successfully fulfilled by the service. * * @param response autofill information for that activity, or {@code null} when the activity * cannot be autofilled (for example, if it only contains read-only fields). See @@ -57,9 +56,8 @@ public final class FillCallback { /** * Notifies the Android System that an - * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure, - * Bundle, int, android.os.CancellationSignal, FillCallback)} - * could not be fulfilled by the service. + * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, + * FillCallback)} could not be fulfilled by the service. * * @param message error message to be displayed to the user. */ diff --git a/core/java/android/service/autofill/FillEventHistory.aidl b/core/java/android/service/autofill/FillEventHistory.aidl new file mode 100644 index 000000000000..3c4852423d1b --- /dev/null +++ b/core/java/android/service/autofill/FillEventHistory.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +parcelable FillEventHistory; diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java new file mode 100644 index 000000000000..3d72fccece9c --- /dev/null +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.autofill.AutofillId; +import android.widget.RemoteViews; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Describes what happened after the latest call to {@link FillCallback#onSuccess(FillResponse)}. + */ +public final class FillEventHistory implements Parcelable { + /** + * Not in parcel. The UID of the {@link AutofillService} that created the {@link FillResponse}. + */ + private final int mServiceUid; + + @Nullable private final Bundle mClientState; + @Nullable List<Event> mEvents; + + /** + * Gets the UID of the {@link AutofillService} that created the {@link FillResponse}. + * + * @return The UID of the {@link AutofillService} + * + * @hide + */ + public int getServiceUid() { + return mServiceUid; + } + + /** + * Returns the client state of the {@link FillResponse}. + * + * @return The client state set by the last {@link FillResponse} + */ + @Nullable public Bundle getClientState() { + return mClientState; + } + + /** + * Returns the events occurred after the latest call to + * {@link FillCallback#onSuccess(FillResponse)}. + * + * @return The list of events or {@code null} if non occurred. + */ + @Nullable public List<Event> getEvents() { + return mEvents; + } + + /** + * @hide + */ + public void addEvent(Event event) { + if (mEvents == null) { + mEvents = new ArrayList<>(1); + } + mEvents.add(event); + } + + /** + * @hide + */ + public FillEventHistory(int serviceUid, @Nullable Bundle clientState) { + mClientState = clientState; + mServiceUid = serviceUid; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBundle(mClientState); + + if (mEvents == null) { + dest.writeInt(0); + } else { + dest.writeInt(mEvents.size()); + + int numEvents = mEvents.size(); + for (int i = 0; i < numEvents; i++) { + Event event = mEvents.get(i); + dest.writeInt(event.getType()); + dest.writeString(event.getDatasetId()); + } + } + } + + /** + * Description of an event that occured after the latest call to + * {@link FillCallback#onSuccess(FillResponse)}. + */ + public static final class Event { + /** + * A dataset was selected. The dataset selected can be read from {@link #getDatasetId()}. + */ + public static final int TYPE_DATASET_SELECTED = 0; + + /** + * A {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was + * selected. The dataset authenticated can be read from {@link #getDatasetId()}. + */ + public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; + + /** + * A {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews) + * fill response authentication} was selected. + */ + public static final int TYPE_AUTHENTICATION_SELECTED = 2; + + /** A save UI was shown. */ + public static final int TYPE_SAVE_SHOWN = 3; + + /** @hide */ + @IntDef( + value = {TYPE_DATASET_SELECTED, + TYPE_DATASET_AUTHENTICATION_SELECTED, + TYPE_AUTHENTICATION_SELECTED, + TYPE_SAVE_SHOWN}) + @Retention(RetentionPolicy.SOURCE) + @interface EventIds{} + + @EventIds private final int mEventType; + @Nullable private final String mDatasetId; + + /** + * Returns the type of the event. + * + * @return The type of the event + */ + public int getType() { + return mEventType; + } + + /** + * Returns the id of dataset the id was on. + * + * @return The id of dataset, or {@code null} the event is not associated with a dataset. + */ + @Nullable public String getDatasetId() { + return mDatasetId; + } + + /** + * Creates a new event. + * + * @param eventType The type of the event + * @param datasetId The dataset the event was on, or {@code null} if the event was on the + * whole response. + * + * @hide + */ + public Event(int eventType, String datasetId) { + mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_SAVE_SHOWN, + "eventType"); + mDatasetId = datasetId; + } + } + + public static final Parcelable.Creator<FillEventHistory> CREATOR = + new Parcelable.Creator<FillEventHistory>() { + @Override + public FillEventHistory createFromParcel(Parcel parcel) { + FillEventHistory selection = new FillEventHistory(0, parcel.readBundle()); + + int numEvents = parcel.readInt(); + for (int i = 0; i < numEvents; i++) { + selection.addEvent(new Event(parcel.readInt(), parcel.readString())); + } + + return selection; + } + + @Override + public FillEventHistory[] newArray(int size) { + return new FillEventHistory[size]; + } + }; +} diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 8c8060a608f0..002536517a87 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -32,8 +32,7 @@ import java.util.ArrayList; /** * Response for a {@link - * AutofillService#onFillRequest(android.app.assist.AssistStructure, - * Bundle, int, android.os.CancellationSignal, FillCallback)}. + * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}. * * <p>The response typically contains one or more {@link Dataset}s, each representing a set of * fields that can be autofilled together, and the Android system displays a dataset picker UI @@ -258,6 +257,19 @@ public final class FillResponse implements Parcelable { } /** + * Specifies views that should not trigger new + * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, + * FillCallback)} requests. + * + * <p>This is typically used when the service cannot autofill the view; for example, an + * {@code EditText} representing a captcha. + */ + public Builder setIgnoredIds(AutofillId...ids) { + // TODO: implement + return this; + } + + /** * Adds a new {@link Dataset} to this response. * * @return This builder. @@ -289,6 +301,9 @@ public final class FillResponse implements Parcelable { return this; } + /** + * @deprecated Use {@link #setClientState(Bundle)} instead. + */ @Deprecated public Builder setExtras(@Nullable Bundle extras) { throwIfDestroyed(); @@ -299,11 +314,9 @@ public final class FillResponse implements Parcelable { /** * Sets a {@link Bundle state} that will be passed to subsequent APIs that * manipulate this response. For example, they are passed to subsequent - * calls to {@link AutofillService#onFillRequest( - * android.app.assist.AssistStructure, Bundle, int, - * android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest( - * android.app.assist.AssistStructure, Bundle, SaveCallback)}. You can use - * this to store intermediate state that is persistent across multiple + * calls to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, + * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}. + * You can use this to store intermediate state that is persistent across multiple * fill requests and the subsequent save request. * * <p>If this method is called on multiple {@link FillResponse} objects for the same diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java index 2c4ba6c57213..3a7013841729 100644 --- a/core/java/android/service/autofill/SaveCallback.java +++ b/core/java/android/service/autofill/SaveCallback.java @@ -17,7 +17,6 @@ package android.service.autofill; import android.app.Activity; -import android.os.Bundle; import android.os.RemoteException; /** @@ -35,8 +34,8 @@ public final class SaveCallback { /** * Notifies the Android System that an - * {@link AutofillService#onSaveRequest (android.app.assist.AssistStructure, Bundle, - * SaveCallback)} was successfully fulfilled by the service. + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully fulfilled + * by the service. * * @throws RuntimeException if an error occurred while calling the Android System. */ @@ -52,8 +51,8 @@ public final class SaveCallback { /** * Notifies the Android System that an - * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, - * SaveCallback)} could not be fulfilled by the service. + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be fulfilled + * by the service. * * @param message error message to be displayed to the user. * diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 258d257813ca..f7964447fb7b 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -21,6 +21,7 @@ import static android.view.autofill.Helper.DEBUG; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.assist.AssistStructure; import android.content.IntentSender; import android.os.Bundle; import android.os.Parcel; @@ -39,7 +40,7 @@ import java.util.Arrays; /** * Information used to indicate that an {@link AutofillService} is interested on saving the * user-inputed data for future use, through a - * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)} + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} * call. * * <p>A {@link SaveInfo} is always associated with a {@link FillResponse}, and it contains at least @@ -93,7 +94,7 @@ import java.util.Arrays; * </pre> * * The - * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)} + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} * is triggered after a call to {@link AutofillManager#commit()}, but only when all conditions * below are met: * @@ -152,12 +153,27 @@ public final class SaveInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface SaveDataType{} + /** + * Usually {@link AutofillService#onSaveRequest(AssistStructure, Bundle, SaveCallback)} + * is called once the activity finishes. If this flag is set it is called once all saved views + * become invisible. + */ + public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1; + + /** @hide */ + @IntDef( + flag = true, + value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}) + @Retention(RetentionPolicy.SOURCE) + @interface SaveInfoFlags{} + private final @SaveDataType int mType; private final CharSequence mNegativeActionTitle; private final IntentSender mNegativeActionListener; private final AutofillId[] mRequiredIds; private final AutofillId[] mOptionalIds; private final CharSequence mDescription; + private final int mFlags; private SaveInfo(Builder builder) { mType = builder.mType; @@ -166,6 +182,7 @@ public final class SaveInfo implements Parcelable { mRequiredIds = builder.mRequiredIds; mOptionalIds = builder.mOptionalIds; mDescription = builder.mDescription; + mFlags = builder.mFlags; } /** @hide */ @@ -194,6 +211,11 @@ public final class SaveInfo implements Parcelable { } /** @hide */ + public @SaveInfoFlags int getFlags() { + return mFlags; + } + + /** @hide */ public CharSequence getDescription() { return mDescription; } @@ -211,6 +233,7 @@ public final class SaveInfo implements Parcelable { private AutofillId[] mOptionalIds; private CharSequence mDescription; private boolean mDestroyed; + private int mFlags; /** * Creates a new builder. @@ -259,6 +282,19 @@ public final class SaveInfo implements Parcelable { } /** + * Set flags changing the save behavior. + * + * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or 0. + * @return This builder. + */ + public @NonNull Builder setFlags(@SaveInfoFlags int flags) { + throwIfDestroyed(); + + mFlags = Preconditions.checkFlagsArgument(flags, FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE); + return this; + } + + /** * Sets the ids of additional, optional views the service would be interested to save. * * <p>See {@link SaveInfo} for more info. @@ -354,6 +390,7 @@ public final class SaveInfo implements Parcelable { .append(", requiredIds=").append(Arrays.toString(mRequiredIds)) .append(", optionalIds=").append(Arrays.toString(mOptionalIds)) .append(", description=").append(mDescription) + .append(", mFlags=").append(mFlags) .append("]").toString(); } @@ -374,6 +411,7 @@ public final class SaveInfo implements Parcelable { parcel.writeParcelable(mNegativeActionListener, flags); parcel.writeParcelableArray(mOptionalIds, flags); parcel.writeCharSequence(mDescription); + parcel.writeInt(mFlags); } public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() { @@ -387,6 +425,7 @@ public final class SaveInfo implements Parcelable { builder.setNegativeAction(parcel.readCharSequence(), parcel.readParcelable(null)); builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class)); builder.setDescription(parcel.readCharSequence()); + builder.setFlags(parcel.readInt()); return builder.build(); } diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index dc1a70d74d7f..ed44f2599695 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -18,6 +18,7 @@ package android.service.notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.os.UserHandle; import android.service.notification.IStatusBarNotificationHolder; import android.service.notification.StatusBarNotification; import android.service.notification.NotificationRankingUpdate; @@ -36,8 +37,8 @@ oneway interface INotificationListener void onInterruptionFilterChanged(int interruptionFilter); // companion device managers only - void onNotificationChannelModification(String pkgName, in NotificationChannel channel, int modificationType); - void onNotificationChannelGroupModification(String pkgName, in NotificationChannelGroup group, int modificationType); + void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType); + void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType); // rankers only void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder); diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 4833be335c88..00bd30456c9e 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -49,6 +49,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -231,25 +232,25 @@ public abstract class NotificationListenerService extends Service { /** * Channel or group modification reason provided to - * {@link #onNotificationChannelModified(String, NotificationChannel, int)} or - * {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the - * provided object was created. + * {@link #onNotificationChannelModified(String, UserHandle,NotificationChannel, int)} or + * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup, + * int)}- the provided object was created. */ public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; /** * Channel or group modification reason provided to - * {@link #onNotificationChannelModified(String, NotificationChannel, int)} or - * {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the - * provided object was updated. + * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or + * {@link #onNotificationChannelGroupModified(String, UserHandle,NotificationChannelGroup, int)} + * - the provided object was updated. */ public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; /** * Channel or group modification reason provided to - * {@link #onNotificationChannelModified(String, NotificationChannel, int)} or - * {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the - * provided object was deleted. + * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or + * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup, + * int)}- the provided object was deleted. */ public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; @@ -432,13 +433,14 @@ public abstract class NotificationListenerService extends Service { * device} in order to receive this callback. * * @param pkg The package the channel belongs to. + * @param user The user on which the change was made. * @param channel The channel that has changed. * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}. */ - public void onNotificationChannelModified(String pkg, NotificationChannel channel, - @ChannelOrGroupModificationTypes int modificationType) { + public void onNotificationChannelModified(String pkg, UserHandle user, + NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) { // optional } @@ -449,13 +451,14 @@ public abstract class NotificationListenerService extends Service { * device} in order to receive this callback. * * @param pkg The package the group belongs to. + * @param user The user on which the change was made. * @param group The group that has changed. * @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED}, * {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}. */ - public void onNotificationChannelGroupModified(String pkg, NotificationChannelGroup group, - @ChannelOrGroupModificationTypes int modificationType) { + public void onNotificationChannelGroupModified(String pkg, UserHandle user, + NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) { // optional } @@ -661,21 +664,24 @@ public abstract class NotificationListenerService extends Service { /** - * Updates a notification channel for a given package. This should only be used to reflect - * changes a user has made to the channel via the listener's user interface. + * Updates a notification channel for a given package for a given user. This should only be used + * to reflect changes a user has made to the channel via the listener's user interface. * + * <p>This method will throw a security exception if you don't have access to notifications + * for the given user.</p> * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to use this method. * * @param pkg The package the channel belongs to. + * @param user The user the channel belongs to. * @param channel the channel to update. */ - public final void updateNotificationChannel(@NonNull String pkg, + public final void updateNotificationChannel(@NonNull String pkg, @NonNull UserHandle user, @NonNull NotificationChannel channel) { if (!isBound()) return; try { getNotificationInterface().updateNotificationChannelFromPrivilegedListener( - mWrapper, pkg, channel); + mWrapper, pkg, user, channel); } catch (RemoteException e) { Log.v(TAG, "Unable to contact notification manager", e); throw e.rethrowFromSystemServer(); @@ -683,19 +689,22 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns all notification channels belonging to the given package. + * Returns all notification channels belonging to the given package for a given user. * + * <p>This method will throw a security exception if you don't have access to notifications + * for the given user.</p> * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to use this method. * * @param pkg The package to retrieve channels for. */ - public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg) { + public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg, + @NonNull UserHandle user) { if (!isBound()) return null; try { return getNotificationInterface().getNotificationChannelsFromPrivilegedListener( - mWrapper, pkg).getList(); + mWrapper, pkg, user).getList(); } catch (RemoteException e) { Log.v(TAG, "Unable to contact notification manager", e); throw e.rethrowFromSystemServer(); @@ -703,19 +712,22 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns all notification channel groups belonging to the given package. + * Returns all notification channel groups belonging to the given package for a given user. * + * <p>This method will throw a security exception if you don't have access to notifications + * for the given user.</p> * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated * device} in order to use this method. * * @param pkg The package to retrieve channel groups for. */ - public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg) { + public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg, + @NonNull UserHandle user) { if (!isBound()) return null; try { return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener( - mWrapper, pkg).getList(); + mWrapper, pkg, user).getList(); } catch (RemoteException e) { Log.v(TAG, "Unable to contact notification manager", e); throw e.rethrowFromSystemServer(); @@ -1252,24 +1264,27 @@ public abstract class NotificationListenerService extends Service { } @Override - public void onNotificationChannelModification(String pkgName, NotificationChannel channel, + public void onNotificationChannelModification(String pkgName, UserHandle user, + NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) { SomeArgs args = SomeArgs.obtain(); args.arg1 = pkgName; - args.arg2 = channel; - args.arg3 = modificationType; + args.arg2 = user; + args.arg3 = channel; + args.arg4 = modificationType; mHandler.obtainMessage( MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget(); } @Override - public void onNotificationChannelGroupModification(String pkgName, + public void onNotificationChannelGroupModification(String pkgName, UserHandle user, NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) { SomeArgs args = SomeArgs.obtain(); args.arg1 = pkgName; - args.arg2 = group; - args.arg3 = modificationType; + args.arg2 = user; + args.arg3 = group; + args.arg4 = modificationType; mHandler.obtainMessage( MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget(); } @@ -1841,17 +1856,19 @@ public abstract class NotificationListenerService extends Service { case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: { SomeArgs args = (SomeArgs) msg.obj; String pkgName = (String) args.arg1; - NotificationChannel channel = (NotificationChannel) args.arg2; - int modificationType = (int) args.arg3; - onNotificationChannelModified(pkgName, channel, modificationType); + UserHandle user= (UserHandle) args.arg2; + NotificationChannel channel = (NotificationChannel) args.arg3; + int modificationType = (int) args.arg4; + onNotificationChannelModified(pkgName, user, channel, modificationType); } break; case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: { SomeArgs args = (SomeArgs) msg.obj; String pkgName = (String) args.arg1; - NotificationChannelGroup group = (NotificationChannelGroup) args.arg2; - int modificationType = (int) args.arg3; - onNotificationChannelGroupModified(pkgName, group, modificationType); + UserHandle user = (UserHandle) args.arg2; + NotificationChannelGroup group = (NotificationChannelGroup) args.arg3; + int modificationType = (int) args.arg4; + onNotificationChannelGroupModified(pkgName, user, group, modificationType); } break; } } diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java new file mode 100644 index 000000000000..b07942ff7f9d --- /dev/null +++ b/core/java/android/util/IconDrawableFactory.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.util; + +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Utility class to load app drawables with appropriate badging. + * + * @hide + */ +public class IconDrawableFactory { + + protected final Context mContext; + protected final PackageManager mPm; + protected final UserManager mUm; + protected final LauncherIcons mLauncherIcons; + protected final boolean mEmbedShadow; + + private IconDrawableFactory(Context context, boolean embedShadow) { + mContext = context; + mPm = context.getPackageManager(); + mUm = context.getSystemService(UserManager.class); + mLauncherIcons = new LauncherIcons(context); + mEmbedShadow = embedShadow; + } + + protected boolean needsBadging(ApplicationInfo appInfo, @UserIdInt int userId) { + return appInfo.isInstantApp() || mUm.isManagedProfile(userId); + } + + public Drawable getBadgedIcon(ApplicationInfo appInfo) { + return getBadgedIcon(appInfo, UserHandle.getUserId(appInfo.uid)); + } + + public Drawable getBadgedIcon(ApplicationInfo appInfo, @UserIdInt int userId) { + return getBadgedIcon(appInfo, appInfo, userId); + } + + public Drawable getBadgedIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo, + @UserIdInt int userId) { + Drawable icon = mPm.loadUnbadgedItemIcon(itemInfo, appInfo); + if (!mEmbedShadow && !needsBadging(appInfo, userId)) { + return icon; + } + + // Before badging, add shadow to adaptive icon if needed. + icon = mLauncherIcons.wrapIconDrawableWithShadow(icon); + if (appInfo.isInstantApp()) { + int badgeColor = Resources.getSystem().getColor( + com.android.internal.R.color.instant_app_badge, null); + icon = mLauncherIcons.getBadgedDrawable(icon, + com.android.internal.R.drawable.ic_instant_icon_badge_bolt, + badgeColor); + } + if (mUm.isManagedProfile(userId)) { + icon = mLauncherIcons.getBadgedDrawable(icon, + com.android.internal.R.drawable.ic_corp_icon_badge_case, + getUserBadgeColor(mUm, userId)); + } + return icon; + } + + // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles() + @VisibleForTesting + public static final int[] CORP_BADGE_COLORS = new int[] { + com.android.internal.R.color.profile_badge_1, + com.android.internal.R.color.profile_badge_2, + com.android.internal.R.color.profile_badge_3 + }; + + public static int getUserBadgeColor(UserManager um, @UserIdInt int userId) { + int badge = um.getManagedProfileBadge(userId); + if (badge < 0) { + badge = 0; + } + int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length]; + return Resources.getSystem().getColor(resourceId, null); + } + + public static IconDrawableFactory newInstance(Context context) { + return new IconDrawableFactory(context, true); + } + + public static IconDrawableFactory newInstance(Context context, boolean embedShadow) { + return new IconDrawableFactory(context, embedShadow); + } +} diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java index 89b9646e54b3..402bef914421 100644 --- a/core/java/android/util/LauncherIcons.java +++ b/core/java/android/util/LauncherIcons.java @@ -21,10 +21,11 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Rect; import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.DrawableWrapper; +import android.graphics.drawable.LayerDrawable; /** * Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons. @@ -32,78 +33,152 @@ import android.graphics.drawable.Drawable; */ public final class LauncherIcons { - private final Paint mPaint = new Paint(); - private final Canvas mCanvas = new Canvas(); + // Percent of actual icon size + private static final float ICON_SIZE_BLUR_FACTOR = 0.5f/48; + // Percent of actual icon size + private static final float ICON_SIZE_KEY_SHADOW_DELTA_FACTOR = 1f/48; private static final int KEY_SHADOW_ALPHA = 61; private static final int AMBIENT_SHADOW_ALPHA = 30; - private static final float BLUR_FACTOR = 0.5f / 48; - private int mShadowInset; - private Bitmap mShadowBitmap; - private int mIconSize; - private Resources mRes; + + private final SparseArray<Bitmap> mShadowCache = new SparseArray<>(); + private final int mIconSize; + private final Resources mRes; public LauncherIcons(Context context) { mRes = context.getResources(); - DisplayMetrics metrics = mRes.getDisplayMetrics(); - mShadowInset = (int)(2 * metrics.density); - mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, - Paint.FILTER_BITMAP_FLAG)); - mIconSize = (int) mRes.getDimensionPixelSize(android.R.dimen.app_icon_size); - } - - /** - * Draw the drawable into a bitmap. - */ - public Bitmap createIconBitmap(Drawable icon) { - final Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888); - mPaint.setAlpha(255); - mCanvas.setBitmap(bitmap); - int iconInset = 0; - if (mShadowBitmap != null) { - mCanvas.drawBitmap(mShadowBitmap, 0, 0, mPaint); - iconInset = mShadowInset; - } - - icon.setBounds(iconInset, iconInset, mIconSize - iconInset, - mIconSize - iconInset); - icon.draw(mCanvas); - mCanvas.setBitmap(null); - return bitmap; + mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size); } public Drawable wrapIconDrawableWithShadow(Drawable drawable) { if (!(drawable instanceof AdaptiveIconDrawable)) { return drawable; } - AdaptiveIconDrawable d = - (AdaptiveIconDrawable) drawable.getConstantState().newDrawable().mutate(); - getShadowBitmap(d); - Bitmap iconbitmap = createIconBitmap(d); - return new BitmapDrawable(mRes, iconbitmap); + Bitmap shadow = getShadowBitmap((AdaptiveIconDrawable) drawable); + return new ShadowDrawable(shadow, drawable); } private Bitmap getShadowBitmap(AdaptiveIconDrawable d) { - if (mShadowBitmap != null) { - return mShadowBitmap; + int shadowSize = Math.max(mIconSize, d.getIntrinsicHeight()); + synchronized (mShadowCache) { + Bitmap shadow = mShadowCache.get(shadowSize); + if (shadow != null) { + return shadow; + } } - mShadowBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ALPHA_8); - mCanvas.setBitmap(mShadowBitmap); + d.setBounds(0, 0, shadowSize, shadowSize); - // Draw key shadow - mPaint.setColor(Color.TRANSPARENT); - float blur = BLUR_FACTOR * mIconSize; - mPaint.setShadowLayer(blur, 0, mShadowInset, KEY_SHADOW_ALPHA << 24); - d.setBounds(mShadowInset, mShadowInset, mIconSize - mShadowInset, mIconSize - mShadowInset); - mCanvas.drawPath(d.getIconMask(), mPaint); + float blur = ICON_SIZE_BLUR_FACTOR * shadowSize; + float keyShadowDistance = ICON_SIZE_KEY_SHADOW_DELTA_FACTOR * shadowSize; + + int bitmapSize = (int) (shadowSize + 2 * blur + keyShadowDistance); + Bitmap shadow = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888); + + Canvas canvas = new Canvas(shadow); + canvas.translate(blur + keyShadowDistance / 2, blur); + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.TRANSPARENT); // Draw ambient shadow - mPaint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24); - d.setBounds(mShadowInset, 2 * mShadowInset, mIconSize - mShadowInset, mIconSize); - mCanvas.drawPath(d.getIconMask(), mPaint); - mPaint.clearShadowLayer(); + paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24); + canvas.drawPath(d.getIconMask(), paint); + + // Draw key shadow + canvas.translate(0, keyShadowDistance); + paint.setShadowLayer(blur, 0, 0, KEY_SHADOW_ALPHA << 24); + canvas.drawPath(d.getIconMask(), paint); + + canvas.setBitmap(null); + synchronized (mShadowCache) { + mShadowCache.put(shadowSize, shadow); + } + return shadow; + } + + public Drawable getBadgeDrawable(int foregroundRes, int backgroundColor) { + return getBadgedDrawable(null, foregroundRes, backgroundColor); + } + + public Drawable getBadgedDrawable(Drawable base, int foregroundRes, int backgroundColor) { + Resources sysRes = Resources.getSystem(); + + Drawable badgeShadow = sysRes.getDrawable( + com.android.internal.R.drawable.ic_corp_icon_badge_shadow); + + Drawable badgeColor = sysRes.getDrawable( + com.android.internal.R.drawable.ic_corp_icon_badge_color) + .getConstantState().newDrawable().mutate(); + badgeColor.setTint(backgroundColor); - return mShadowBitmap; + Drawable badgeForeground = sysRes.getDrawable(foregroundRes); + + Drawable[] drawables = base == null + ? new Drawable[] {badgeShadow, badgeColor, badgeForeground } + : new Drawable[] {base, badgeShadow, badgeColor, badgeForeground }; + return new LayerDrawable(drawables); + } + + /** + * A drawable which draws a shadow bitmap behind a drawable + */ + private static class ShadowDrawable extends DrawableWrapper { + + final MyConstantState mState; + + public ShadowDrawable(Bitmap shadow, Drawable dr) { + super(dr); + mState = new MyConstantState(shadow, dr.getConstantState()); + } + + ShadowDrawable(MyConstantState state) { + super(state.mChildState.newDrawable()); + mState = state; + } + + @Override + public ConstantState getConstantState() { + return mState; + } + + @Override + public void draw(Canvas canvas) { + Rect bounds = getBounds(); + canvas.drawBitmap(mState.mShadow, null, bounds, mState.mPaint); + canvas.save(); + // Ratio of child drawable size to shadow bitmap size + float factor = 1 / (1 + 2 * ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR); + + canvas.translate( + bounds.width() * factor * + (ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR / 2), + bounds.height() * factor * ICON_SIZE_BLUR_FACTOR); + canvas.scale(factor, factor); + super.draw(canvas); + canvas.restore(); + } + + private static class MyConstantState extends ConstantState { + + final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + final Bitmap mShadow; + final ConstantState mChildState; + + MyConstantState(Bitmap shadow, ConstantState childState) { + mShadow = shadow; + mChildState = childState; + } + + @Override + public Drawable newDrawable() { + return new ShadowDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChildState.getChangingConfigurations(); + } + } } } diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index f47c35580f0f..1ccf16a64ade 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -127,20 +127,23 @@ public class FocusFinder { if (focused == null || focused == root) { return root; } - ViewParent effective = focused.getParent(); + ViewGroup effective = null; + ViewParent nextParent = focused.getParent(); do { - if (effective == root) { - return root; + if (nextParent == root) { + return effective != null ? effective : root; } - ViewGroup vg = (ViewGroup) effective; + ViewGroup vg = (ViewGroup) nextParent; if (vg.getTouchscreenBlocksFocus() && focused.getContext().getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN) && vg.isKeyboardNavigationCluster()) { - return vg; + // Don't stop and return here because the cluster could be nested and we only + // care about the top-most one. + effective = vg; } - effective = effective.getParent(); - } while (effective != null); + nextParent = nextParent.getParent(); + } while (nextParent instanceof ViewGroup); return root; } diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index 2fe98c0b6f05..dbeb747adfba 100644 --- a/core/java/android/view/IPinnedStackController.aidl +++ b/core/java/android/view/IPinnedStackController.aidl @@ -28,4 +28,9 @@ interface IPinnedStackController { * Notifies the controller that the PiP is currently minimized. */ oneway void setIsMinimized(boolean isMinimized); + + /** + * @return what WM considers to be the current device rotation. + */ + int getDisplayRotation(); } diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index 782f3499fb79..9382741a81ec 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -41,9 +41,13 @@ oneway interface IPinnedStackListener { * current state with the aspect ratio applied. The {@param animatingBounds} are provided * to indicate the current target bounds of the pinned stack (the final bounds if animating, * the current bounds if not), which may be helpful in calculating dependent animation bounds. + * + * The {@param displayRotation} is provided so that the client can verify when making certain + * calls that it will not provide stale information based on an old display rotation (ie. if + * the WM has changed in the mean time but the client has not received onMovementBoundsChanged). */ void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds, - boolean fromImeAdjustement); + boolean fromImeAdjustement, int displayRotation); /** * Called when window manager decides to adjust the pinned stack bounds because of the IME, or diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 1a71679449a4..9d40895f6ad9 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -295,7 +295,15 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb @Override protected void onDetachedFromWindow() { - getViewRootImpl().removeWindowStoppedCallback(this); + ViewRootImpl viewRoot = getViewRootImpl(); + // It's possible to create a SurfaceView using the default constructor and never + // attach it to a view hierarchy, this is a common use case when dealing with + // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage + // the lifecycle. Instead of attaching it to a view, he/she can just pass + // the SurfaceHolder forward, most live wallpapers do it. + if (viewRoot != null) { + viewRoot.removeWindowStoppedCallback(this); + } mAttachedToWindow = false; if (mGlobalListenersAdded) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 172ad8da5381..9ab6775310f4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -66,6 +66,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; @@ -1134,27 +1135,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @IntDef({ IMPORTANT_FOR_AUTOFILL_AUTO, IMPORTANT_FOR_AUTOFILL_YES, - IMPORTANT_FOR_AUTOFILL_NO + IMPORTANT_FOR_AUTOFILL_NO, + IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, + IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS }) @Retention(RetentionPolicy.SOURCE) public @interface AutofillImportance {} /** - * Automatically determine whether a view is important for auto-fill. + * Automatically determine whether a view is important for autofill. */ public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0; /** - * The view is important for important for auto-fill. + * The view is important for autofill, and its children (if any) will be traversed. */ public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1; /** - * The view is not important for auto-fill. + * The view is not important for autofill, and its children (if any) will be traversed. */ public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2; /** + * The view is important for autofill, but its children (if any) will not be traversed. + */ + public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4; + + /** + * The view is not important for autofill, and its children (if any) will not be traversed. + */ + public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8; + + /** * This view is enabled. Interpretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. * {@hide} @@ -3874,6 +3887,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private Drawable mDefaultFocusHighlight; private Drawable mDefaultFocusHighlightCache; private boolean mDefaultFocusHighlightSizeChanged; + /** + * True if the default focus highlight is needed on the target device. + */ + private static boolean sUseDefaultFocusHighlight; private String mTransitionName; @@ -4380,6 +4397,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Nullable private RoundScrollbarRenderer mRoundScrollbarRenderer; + /** Used to delay visibility updates sent to the autofill manager */ + private Handler mVisibilityChangeForAutofillHandler; + /** * Simple constructor to use when creating a view from code. * @@ -4448,6 +4468,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sAutoFocusableOffUIThreadWontNotifyParents = targetSdkVersion < Build.VERSION_CODES.O; + sUseDefaultFocusHighlight = context.getResources().getBoolean( + com.android.internal.R.bool.config_useDefaultFocusHighlight); + sCompatibilityDone = true; } } @@ -7564,7 +7587,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * should use {@link #setImportantForAutofill(int)} instead. * * <p><strong>Note:</strong> returning {@code false} does not guarantee the view will be - * excluded from the structure; for example, if the user explicitly requested auto-fill, the + * excluded from the structure; for example, if the user explicitly requested autofill, the * View might be always included. * * <p>This decision applies just for the view, not its children - if the view children are not @@ -9716,7 +9739,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.IntToString(from = NOT_FOCUSABLE, to = "NOT_FOCUSABLE"), @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"), @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO") - }) + }, category = "focus") @Focusable public int getFocusable() { return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE; @@ -9730,7 +9753,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return Whether the view is focusable in touch mode. * @attr ref android.R.styleable#View_focusableInTouchMode */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "focus") public final boolean isFocusableInTouchMode() { return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE); } @@ -9758,12 +9781,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if this view is a root of a cluster, or false otherwise. * @attr ref android.R.styleable#View_keyboardNavigationCluster */ - @ViewDebug.ExportedProperty(category = "keyboardNavigationCluster") + @ViewDebug.ExportedProperty(category = "focus") public final boolean isKeyboardNavigationCluster() { return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0; } /** + * Searches up the view hierarchy to find the top-most cluster. All deeper/nested clusters + * will be ignored. + * + * @return the keyboard navigation cluster that this view is in (can be this view) + * or {@code null} if not in one + */ + View findKeyboardNavigationCluster() { + if (mParent instanceof View) { + View cluster = ((View) mParent).findKeyboardNavigationCluster(); + if (cluster != null) { + return cluster; + } else if (isKeyboardNavigationCluster()) { + return this; + } + } + return null; + } + + /** * Set whether this view is a root of a keyboard navigation cluster. * * @param isCluster If true, this view is a root of a cluster. @@ -9785,9 +9827,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - public void setFocusedInCluster() { - if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).setFocusInCluster(this); + public final void setFocusedInCluster() { + View top = findKeyboardNavigationCluster(); + if (top == this) { + return; + } + ViewParent parent = mParent; + View child = this; + while (parent instanceof ViewGroup) { + ((ViewGroup) parent).setFocusedInCluster(child); + if (parent == top) { + return; + } + child = (View) parent; + parent = parent.getParent(); } } @@ -9803,7 +9856,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return {@code true} if this view is the default-focus view, {@code false} otherwise * @attr ref android.R.styleable#View_focusedByDefault */ - @ViewDebug.ExportedProperty(category = "focusedByDefault") + @ViewDebug.ExportedProperty(category = "focus") public final boolean isFocusedByDefault() { return (mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0; } @@ -9916,7 +9969,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if this View should use a default focus highlight. * @attr ref android.R.styleable#View_defaultFocusHighlightEnabled */ - @ViewDebug.ExportedProperty(category = "defaultFocusHighlightEnabled") + @ViewDebug.ExportedProperty(category = "focus") public final boolean getDefaultFocusHighlightEnabled() { return mDefaultFocusHighlightEnabled; } @@ -11696,6 +11749,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (fg != null && isVisible != fg.isVisible()) { fg.setVisible(isVisible, false); } + + if (isAutofillable()) { + AutofillManager afm = getAutofillManager(); + + if (afm != null && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID) { + if (mVisibilityChangeForAutofillHandler != null) { + mVisibilityChangeForAutofillHandler.removeMessages(0); + } + + // If the view is in the background but still part of the hierarchy this is called + // with isVisible=false. Hence visibility==false requires further checks + if (isVisible) { + afm.notifyViewVisibilityChange(this, true); + } else { + if (mVisibilityChangeForAutofillHandler == null) { + mVisibilityChangeForAutofillHandler = + new VisibilityChangeForAutofillHandler(afm, this); + } + // Let current operation (e.g. removal of the view from the hierarchy) + // finish before checking state + mVisibilityChangeForAutofillHandler.obtainMessage(0, this).sendToTarget(); + } + } + } } /** @@ -19607,7 +19684,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final boolean hasFocusStateSpecified = background == null || !background.isStateful() || !background.hasFocusStateSpecified(); return !isInTouchMode() && getDefaultFocusHighlightEnabled() && hasFocusStateSpecified - && isAttachedToWindow(); + && isAttachedToWindow() && sUseDefaultFocusHighlight; } /** @@ -24492,6 +24569,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * When a view becomes invisible checks if autofill considers the view invisible too. This + * happens after the regular removal operation to make sure the operation is finished by the + * time this is called. + */ + private static class VisibilityChangeForAutofillHandler extends Handler { + private final AutofillManager mAfm; + private final View mView; + + private VisibilityChangeForAutofillHandler(@NonNull AutofillManager afm, + @NonNull View view) { + mAfm = afm; + mView = view; + } + + @Override + public void handleMessage(Message msg) { + mAfm.notifyViewVisibilityChange(mView, mView.isShown()); + } + } + + /** * Base class for derived classes that want to save and restore their own * state in {@link android.view.View#onSaveInstanceState()}. */ @@ -25694,6 +25792,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // focus stream.addProperty("focus:hasFocus", hasFocus()); stream.addProperty("focus:isFocused", isFocused()); + stream.addProperty("focus:focusable", getFocusable()); stream.addProperty("focus:isFocusable", isFocusable()); stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode()); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f9eb25da642a..1977ef5ae989 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -807,33 +807,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return mDefaultFocus != null || super.hasDefaultFocus(); } - void setFocusInCluster(View child) { - // Stop at the root of the cluster - if (child.isKeyboardNavigationCluster()) { - return; - } - + void setFocusedInCluster(View child) { mFocusedInCluster = child; - - if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).setFocusInCluster(this); - } } - void clearFocusInCluster(View child) { + /** + * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing + * it. + * <br> + * This is intended to be run on {@code child}'s immediate parent. This is necessary because + * the chain is sometimes cleared after {@code child} has been detached. + */ + void clearFocusedInCluster(View child) { if (mFocusedInCluster != child) { return; } - - if (child.isKeyboardNavigationCluster()) { - return; - } - - mFocusedInCluster = null; - - if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).clearFocusInCluster(this); - } + View top = findKeyboardNavigationCluster(); + ViewParent parent = this; + do { + ((ViewGroup) parent).mFocusedInCluster = null; + parent = parent.getParent(); + } while (parent != top && parent instanceof ViewGroup); } @Override @@ -1281,7 +1275,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) { if (touchscreenBlocksFocus) { mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS; - if (hasFocus()) { + if (hasFocus() && !isKeyboardNavigationCluster()) { final View focusedChild = getDeepestFocusedChild(); if (!focusedChild.isFocusableInTouchMode()) { final View newFocus = focusSearch(FOCUS_FORWARD); @@ -1306,6 +1300,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Check whether this ViewGroup should ignore focus requests for itself and its children. */ + @ViewDebug.ExportedProperty(category = "focus") public boolean getTouchscreenBlocksFocus() { return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0; } @@ -1316,7 +1311,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // cluster, focus is free to move around within it. return getTouchscreenBlocksFocus() && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) - && (!hasFocus() || !isKeyboardNavigationCluster()); + && !(isKeyboardNavigationCluster() + && (hasFocus() || (findKeyboardNavigationCluster() != this))); } @Override @@ -3217,8 +3213,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) { - if (mFocusedInCluster != null && !mFocusedInCluster.isKeyboardNavigationCluster() - && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS + if (mFocusedInCluster != null && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS && (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE && mFocusedInCluster.restoreFocusInCluster(direction)) { return true; @@ -5182,7 +5177,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } if (view == mFocusedInCluster) { - clearFocusInCluster(view); + clearFocusedInCluster(view); } view.clearAccessibilityFocus(); @@ -5302,7 +5297,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearDefaultFocus = view; } if (view == mFocusedInCluster) { - clearFocusInCluster(view); + clearFocusedInCluster(view); } view.clearAccessibilityFocus(); @@ -5458,7 +5453,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearDefaultFocus(child); } if (child == mFocusedInCluster) { - clearFocusInCluster(child); + clearFocusedInCluster(child); } child.clearAccessibilityFocus(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a7ececf6c782..9ecced61c917 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1561,6 +1561,16 @@ public final class ViewRootImpl implements ViewParent, host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */)); } + /** + * @return the last content insets for use in adjusting the source hint rect for the + * picture-in-picture transition. + * + * @hide + */ + public Rect getLastContentInsets() { + return mAttachInfo.mContentInsets; + } + private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) { return lp.type == TYPE_STATUS_BAR_PANEL || lp.type == TYPE_INPUT_METHOD @@ -4665,7 +4675,8 @@ public final class ViewRootImpl implements ViewParent, if (focused == null && mView.restoreDefaultFocus()) { return true; } - View cluster = focused.keyboardNavigationClusterSearch(null, direction); + View cluster = focused == null ? keyboardNavigationClusterSearch(null, direction) + : focused.keyboardNavigationClusterSearch(null, direction); // Since requestFocus only takes "real" focus directions (and therefore also // restoreFocusInCluster), convert forward/backward focus into FOCUS_DOWN. diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index b157709e5261..92b0d9817fc0 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -326,8 +326,8 @@ public abstract class ViewStructure { /** * Sets whether the data on this node is sensitive; if it is, then its content (text, autofill * value, etc..) is striped before calls to {@link - * android.service.autofill.AutofillService#onFillRequest(android.app.assist.AssistStructure, - * Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback)}. + * android.service.autofill.AutofillService#onFillRequest(android.service.autofill.FillRequest, + * android.os.CancellationSignal, android.service.autofill.FillCallback)}. * * <p>By default, all nodes are assumed to be sensitive, and only nodes that does not have PII * (Personally Identifiable Information - sensitive data such as email addresses, credit card @@ -336,8 +336,8 @@ public abstract class ViewStructure { * * <p>Notice that the content of even sensitive nodes are sent to the service (through the * {@link - * android.service.autofill.AutofillService#onSaveRequest(android.app.assist.AssistStructure, - * Bundle, android.service.autofill.SaveCallback)} call) when the user consented to save + * android.service.autofill.AutofillService#onSaveRequest(android.service.autofill.SaveRequest, + * android.service.autofill.SaveCallback)} call) when the user consented to save * thedata, so it is important to set the content of sensitive nodes as well, but mark them as * sensitive. * diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index 6dbc09ceef97..bf0e10f993ad 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -303,13 +303,16 @@ public abstract class WindowManagerInternal { * hidden, no matter how WindowManagerService will react / has reacted * to corresponding API calls. Note that this state is not guaranteed * to be synchronized with state in WindowManagerService. + * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back + * key is expected to dismiss the software keyboard. * @param targetWindowToken token to identify the target window that the IME is associated with. * {@code null} when application, system, or the IME itself decided to * change its window visibility before being associated with any target * window. */ public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken, - boolean imeWindowVisible, @Nullable IBinder targetWindowToken); + boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed, + @Nullable IBinder targetWindowToken); /** * Returns true when the hardware keyboard is available. diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index bb6e0eed57cc..030c78b7f4b6 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1537,6 +1537,18 @@ public interface WindowManagerPolicy { public void setLastInputMethodWindowLw(WindowState ime, WindowState target); /** + * An internal callback (from InputMethodManagerService) to notify a state change regarding + * whether the back key should dismiss the software keyboard (IME) or not. + * + * @param newValue {@code true} if the software keyboard is shown and the back key is expected + * to dismiss the software keyboard. + * @hide + */ + default void setDismissImeOnBackKeyPressed(boolean newValue) { + // Default implementation does nothing. + } + + /** * Show the recents task list app. * @hide */ diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ec6559cba39b..8ed0762a9e92 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -31,7 +31,10 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.RemoteException; +import android.service.autofill.AutofillService; +import android.service.autofill.FillEventHistory; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.view.View; @@ -104,6 +107,7 @@ public final class AutofillManager { * * @deprecated Use {@link android.service.autofill.FillRequest#FLAG_MANUAL_REQUEST} */ + // TODO(b/33197203): remove @Deprecated public static final int FLAG_MANUAL_REQUEST = 0x1; @@ -143,6 +147,10 @@ public final class AutofillManager { @GuardedBy("mLock") @Nullable private ParcelableMap mLastAutofilledData; + /** If view tracking is enabled, contains the tracking state */ + @GuardedBy("mLock") + @Nullable private TrackedViews mTrackedViews; + /** @hide */ public interface AutofillClient { /** @@ -177,6 +185,20 @@ public final class AutofillManager { * @return Whether the UI was hidden. */ boolean autofillCallbackRequestHideFillUi(); + + /** + * Checks if the view is currently attached and visible. + * + * @return {@code true} iff the view is attached or visible + */ + boolean getViewVisibility(int viewId); + + /** + * Checks is the client is currently visible as understood by autofill. + * + * @return {@code true} if the client is currently visible + */ + boolean isVisibleForAutofill(); } /** @@ -260,6 +282,21 @@ public final class AutofillManager { } /** + * Called once the client becomes visible. + * + * @see AutofillClient#isVisibleForAutofill() + * + * {@hide} + */ + public void onVisibleForAutofill() { + synchronized (mLock) { + if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) { + mTrackedViews.onVisibleForAutofill(); + } + } + } + + /** * Save state before activity lifecycle * * @param outState Place to store the state @@ -300,6 +337,20 @@ public final class AutofillManager { } /** + * Should always be called from {@link AutofillService#getFillEventHistory()}. + * + * @hide + */ + @Nullable public FillEventHistory getFillEventHistory() { + try { + return mService.getFillEventHistory(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return null; + } + } + + /** * Explicitly requests a new autofill context. * * <p>Normally, the autofill context is automatically started when autofillable views are @@ -412,6 +463,22 @@ public final class AutofillManager { } /** + * Called when a {@link View view's} visibility changes. + * + * @param view {@link View} that was exited. + * @param isVisible visible if the view is visible in the view hierarchy. + * + * @hide + */ + public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) { + synchronized (mLock) { + if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) { + mTrackedViews.notifyViewVisibilityChange(view, isVisible); + } + } + } + + /** * Called when a virtual view that supports autofill is entered. * * @param view the {@link View} whose descendant is the virtual view. @@ -669,6 +736,7 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } + mTrackedViews = null; mSessionId = NO_SESSION; } @@ -683,6 +751,7 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } + mTrackedViews = null; mSessionId = NO_SESSION; } @@ -903,6 +972,25 @@ public final class AutofillManager { } } + /** + * Set the tracked views. + * + * @param trackedIds The views to be tracked + * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible. + */ + private void setTrackedViews(int sessionId, List<AutofillId> trackedIds, + boolean saveOnAllViewsInvisible) { + synchronized (mLock) { + if (mEnabled && mSessionId == sessionId) { + if (saveOnAllViewsInvisible) { + mTrackedViews = new TrackedViews(trackedIds); + } else { + mTrackedViews = null; + } + } + } + } + private void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) { final View anchor = findAchorView(windowToken, id); @@ -969,6 +1057,195 @@ public final class AutofillManager { } /** + * View tracking information. Once all tracked views become invisible the session is finished. + */ + private class TrackedViews { + /** Visible tracked views */ + @Nullable private ArraySet<AutofillId> mVisibleTrackedIds; + + /** Invisible tracked views */ + @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; + + /** + * Check if set is null or value is in set. + * + * @param set The set or null (== empty set) + * @param value The value that might be in the set + * + * @return {@code true} iff set is not empty and value is in set + */ + private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) { + return set != null && set.contains(value); + } + + /** + * Add a value to a set. If set is null, create a new set. + * + * @param set The set or null (== empty set) + * @param valueToAdd The value to add + * + * @return The set including the new value. If set was {@code null}, a set containing only + * the new value. + */ + @NonNull + private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) { + if (set == null) { + set = new ArraySet<>(1); + } + + set.add(valueToAdd); + + return set; + } + + /** + * Remove a value from a set. + * + * @param set The set or null (== empty set) + * @param valueToRemove The value to remove + * + * @return The set without the removed value. {@code null} if set was null, or is empty + * after removal. + */ + @Nullable + private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) { + if (set == null) { + return null; + } + + set.remove(valueToRemove); + + if (set.isEmpty()) { + return null; + } + + return set; + } + + /** + * Set the tracked views. + * + * @param trackedIds The views to be tracked + */ + TrackedViews(@NonNull List<AutofillId> trackedIds) { + mVisibleTrackedIds = null; + mInvisibleTrackedIds = null; + + AutofillClient client = getClientLocked(); + if (trackedIds != null) { + int numIds = trackedIds.size(); + for (int i = 0; i < numIds; i++) { + AutofillId id = trackedIds.get(i); + + boolean isVisible = true; + if (client != null && client.isVisibleForAutofill()) { + isVisible = client.getViewVisibility(id.getViewId()); + } + + if (isVisible) { + mVisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); + } else { + mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); + } + } + } + + if (DEBUG) { + Log.d(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " + + " mVisibleTrackedIds=" + mVisibleTrackedIds + + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); + } + + if (mVisibleTrackedIds == null) { + finishSessionLocked(); + } + } + + /** + * Called when a {@link View view's} visibility changes. + * + * @param view {@link View} that was exited. + * @param isVisible visible if the view is visible in the view hierarchy. + */ + void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) { + AutofillId id = getAutofillId(view); + AutofillClient client = getClientLocked(); + + if (DEBUG) { + Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible=" + + isVisible); + } + + if (client != null && client.isVisibleForAutofill()) { + if (isVisible) { + if (isInSet(mInvisibleTrackedIds, id)) { + mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); + mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); + } + } else { + if (isInSet(mVisibleTrackedIds, id)) { + mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); + mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); + } + } + } + + if (mVisibleTrackedIds == null) { + finishSessionLocked(); + } + } + + /** + * Called once the client becomes visible. + * + * @see AutofillClient#isVisibleForAutofill() + */ + void onVisibleForAutofill() { + // The visibility of the views might have changed while the client was not started, + // hence update the visibility state for all views. + AutofillClient client = getClientLocked(); + ArraySet<AutofillId> updatedVisibleTrackedIds = null; + ArraySet<AutofillId> updatedInvisibleTrackedIds = null; + if (client != null) { + if (mInvisibleTrackedIds != null) { + for (AutofillId id : mInvisibleTrackedIds) { + if (client.getViewVisibility(id.getViewId())) { + updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); + + if (DEBUG) { + Log.i(TAG, "onVisibleForAutofill() " + id + " became visible"); + } + } else { + updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); + } + } + } + + if (mVisibleTrackedIds != null) { + for (AutofillId id : mVisibleTrackedIds) { + if (client.getViewVisibility(id.getViewId())) { + updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id); + } else { + updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id); + + if (DEBUG) { + Log.i(TAG, "onVisibleForAutofill() " + id + " became invisible"); + } + } + } + } + + mInvisibleTrackedIds = updatedInvisibleTrackedIds; + mVisibleTrackedIds = updatedVisibleTrackedIds; + } + + if (mVisibleTrackedIds == null) { + finishSessionLocked(); + } + } + } + + /** * Callback for auto-fill related events. * * <p>Typically used for applications that display their own "auto-complete" views, so they can @@ -1106,5 +1383,16 @@ public final class AutofillManager { }); } } + + @Override + public void setTrackedViews(int sessionId, List<AutofillId> ids, + boolean saveOnAllViewsInvisible) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.mContext.getMainThreadHandler().post( + () -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible) + ); + } + } } } diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 68b3ccabc8be..df777c4a6869 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -19,6 +19,7 @@ package android.view.autofill; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.service.autofill.FillEventHistory; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; @@ -33,6 +34,7 @@ interface IAutoFillManager { int startSession(IBinder activityToken, IBinder windowToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, String packageName); + FillEventHistory getFillEventHistory(); boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback); void setWindow(int sessionId, in IBinder windowToken); void updateSession(int sessionId, in AutofillId id, in Rect bounds, diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 56f91ed6de9b..1a6bad2d1cd6 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -49,6 +49,13 @@ oneway interface IAutoFillManagerClient { void authenticate(int sessionId, in IntentSender intent, in Intent fillInIntent); /** + * Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible + * the session is finished automatically. + */ + void setTrackedViews(int sessionId, in List<AutofillId> ids, + boolean saveOnAllViewsInvisible); + + /** * Requests showing the fill UI. */ void requestShowFillUi(int sessionId, in IBinder windowToken, in AutofillId id, int width, diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index f634a1b52675..022c15726201 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -53,7 +53,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.StringJoiner; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -89,7 +88,7 @@ final class TextClassifierImpl implements TextClassifier { @Override public TextSelection suggestSelection( @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex, - LocaleList defaultLocales) { + @Nullable LocaleList defaultLocales) { validateInput(text, selectionStartIndex, selectionEndIndex); try { if (text.length() > 0) { @@ -128,7 +127,8 @@ final class TextClassifierImpl implements TextClassifier { @Override public TextClassificationResult getTextClassificationResult( - @NonNull CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) { + @NonNull CharSequence text, int startIndex, int endIndex, + @Nullable LocaleList defaultLocales) { validateInput(text, startIndex, endIndex); try { if (text.length() > 0) { @@ -156,7 +156,8 @@ final class TextClassifierImpl implements TextClassifier { } @Override - public LinksInfo getLinks(CharSequence text, int linkMask, LocaleList defaultLocales) { + public LinksInfo getLinks( + @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) { Preconditions.checkArgument(text != null); try { return LinksInfoFactory.create( @@ -199,12 +200,11 @@ final class TextClassifierImpl implements TextClassifier { @GuardedBy("mSmartSelectionLock") // Do not call outside this lock. @Nullable private Locale findBestSupportedLocaleLocked(LocaleList localeList) { - final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse( - new StringJoiner(",") - // Specified localeList takes priority over the system default - .add(localeList.toLanguageTags()) - .add(LocaleList.getDefault().toLanguageTags()) - .toString()); + // Specified localeList takes priority over the system default, so it is listed first. + final String languages = localeList.isEmpty() + ? LocaleList.getDefault().toLanguageTags() + : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags(); + final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages); return Locale.lookup(languageRangeList, loadModelFilePathsLocked().keySet()); } diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java index 6c6079f202fd..d11c03ad6878 100644 --- a/core/java/android/widget/Chronometer.java +++ b/core/java/android/widget/Chronometer.java @@ -17,11 +17,13 @@ package android.widget; import android.content.Context; +import android.content.Intent; import android.content.res.TypedArray; import android.icu.text.MeasureFormat; import android.icu.text.MeasureFormat.FormatWidth; import android.icu.util.Measure; import android.icu.util.MeasureUnit; +import android.net.Uri; import android.os.SystemClock; import android.text.format.DateUtils; import android.util.AttributeSet; @@ -148,6 +150,22 @@ public class Chronometer extends TextView { } /** + * @return whether this is the final countdown + */ + public boolean isTheFinalCountDown() { + try { + getContext().startActivity( + new Intent(Intent.ACTION_VIEW, Uri.parse("https://youtu.be/9jK-NcRmVcw")) + .addCategory(Intent.CATEGORY_BROWSABLE) + .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT + | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT)); + return true; + } catch (Exception e) { + return false; + } + } + + /** * Set the time that the count-up timer is in reference to. * * @param base Use the {@link SystemClock#elapsedRealtime} time base. diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 4fb7b19f7bf7..0d3baa8361ba 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -146,7 +146,7 @@ public class Editor { private static final String UNDO_OWNER_TAG = "Editor"; // Ordering constants used to place the Action Mode or context menu items in their menu. - private static final int MENU_ITEM_ORDER_ASSIST = 1; + private static final int MENU_ITEM_ORDER_ASSIST = 0; private static final int MENU_ITEM_ORDER_UNDO = 2; private static final int MENU_ITEM_ORDER_REDO = 3; private static final int MENU_ITEM_ORDER_CUT = 4; @@ -156,8 +156,8 @@ public class Editor { private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 8; private static final int MENU_ITEM_ORDER_SELECT_ALL = 9; private static final int MENU_ITEM_ORDER_REPLACE = 10; - private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 11; - private static final int MENU_ITEM_ORDER_AUTOFILL = 12; + private static final int MENU_ITEM_ORDER_AUTOFILL = 11; + private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; // Each Editor manages its own undo stack. private final UndoManager mUndoManager = new UndoManager(); @@ -6322,9 +6322,10 @@ public class Editor { * Adds "PROCESS_TEXT" menu items to the specified menu. */ public void onInitializeMenu(Menu menu) { - int i = 0; + final int size = mSupportedActivities.size(); loadSupportedActivities(); - for (ResolveInfo resolveInfo : mSupportedActivities) { + for (int i = 0; i < size; i++) { + final ResolveInfo resolveInfo = mSupportedActivities.get(i); menu.add(Menu.NONE, Menu.NONE, Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i++, getLabel(resolveInfo)) diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 9245134fd266..036b39100286 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -252,9 +252,7 @@ public class RemoteViews implements Parcelable, Filter { if (mEnterAnimationId != 0) { opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0); } else { - opts = ActivityOptions.makeScaleUpAnimation(view, - 0, 0, - view.getMeasuredWidth(), view.getMeasuredHeight()); + opts = ActivityOptions.makeBasic(); } if (launchStackId != StackId.INVALID_STACK_ID) { diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index a6a9db44c420..127904039506 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -133,6 +133,7 @@ public class TextClock extends TextView { private CharSequence mDescFormat; private boolean mRegistered; + private boolean mShouldRunTicker; private Calendar mTime; private String mTimeZone; @@ -252,8 +253,7 @@ public class TextClock extends TextView { } createTime(mTimeZone); - // Wait until registering for events to handle the ticker - chooseFormat(false); + chooseFormat(); } private void createTime(String timeZone) { @@ -461,16 +461,6 @@ public class TextClock extends TextView { } /** - * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} - * depending on whether the user has selected 24-hour format. - * - * Calling this method does not schedule or unschedule the time ticker. - */ - private void chooseFormat() { - chooseFormat(true); - } - - /** * Returns the current format string. Always valid after constructor has * finished, and will never be {@code null}. * @@ -483,11 +473,8 @@ public class TextClock extends TextView { /** * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} * depending on whether the user has selected 24-hour format. - * - * @param handleTicker true if calling this method should schedule/unschedule the - * time ticker, false otherwise */ - private void chooseFormat(boolean handleTicker) { + private void chooseFormat() { final boolean format24Requested = is24HourModeEnabled(); LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale); @@ -503,7 +490,7 @@ public class TextClock extends TextView { boolean hadSeconds = mHasSeconds; mHasSeconds = DateFormat.hasSeconds(mFormat); - if (handleTicker && mRegistered && hadSeconds != mHasSeconds) { + if (mShouldRunTicker && hadSeconds != mHasSeconds) { if (hadSeconds) getHandler().removeCallbacks(mTicker); else mTicker.run(); } @@ -517,26 +504,44 @@ public class TextClock extends TextView { } @Override - public void onVisibilityAggregated(boolean isVisible) { - if (!mRegistered && isVisible) { + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (!mRegistered) { mRegistered = true; registerReceiver(); registerObserver(); createTime(mTimeZone); + } + } + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + + if (!mShouldRunTicker && isVisible) { + mShouldRunTicker = true; if (mHasSeconds) { mTicker.run(); } else { onTimeChanged(); } - } else if (mRegistered && !isVisible) { + } else if (mShouldRunTicker && !isVisible) { + mShouldRunTicker = false; + getHandler().removeCallbacks(mTicker); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mRegistered) { unregisterReceiver(); unregisterObserver(); - getHandler().removeCallbacks(mTicker); - mRegistered = false; } } @@ -586,10 +591,16 @@ public class TextClock extends TextView { } } + /** + * Update the displayed time if this view and its ancestors and window is visible + */ private void onTimeChanged() { - mTime.setTimeInMillis(System.currentTimeMillis()); - setText(DateFormat.format(mFormat, mTime)); - setContentDescription(DateFormat.format(mDescFormat, mTime)); + // mShouldRunTicker always equals the last value passed into onVisibilityAggregated + if (mShouldRunTicker) { + mTime.setTimeInMillis(System.currentTimeMillis()); + setText(DateFormat.format(mFormat, mTime)); + setContentDescription(DateFormat.format(mDescFormat, mTime)); + } } /** @hide */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f9f10af85b69..f1a3ff54ed80 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8196,9 +8196,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTempTextPaint.set(getPaint()); mTempTextPaint.setTextSize(suggestedSizeInPx); + final int availableWidth = mHorizontallyScrolling + ? VERY_WIDE + : getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight(); final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain( - text, 0, text.length(), mTempTextPaint, - getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight()); + text, 0, text.length(), mTempTextPaint, availableWidth); layoutBuilder.setAlignment(getLayoutAlignment()) .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier()) diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 032c7750c1d6..b9ed96395130 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -34,6 +34,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.widget.ResolverDrawerLayout; import java.util.ArrayList; import java.util.Collections; @@ -56,6 +57,11 @@ public class AccessibilityButtonChooserActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.accessibility_button_chooser); + final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel); + if (rdl != null) { + rdl.setOnDismissedListener(this::finish); + } + String component = Settings.Secure.getString(getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT); if (TextUtils.isEmpty(component)) { diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 6e9e350becd7..79544df8345f 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -17,6 +17,7 @@ package com.android.internal.content; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; @@ -150,14 +151,17 @@ public abstract class FileSystemProvider extends DocumentsProvider { return childId; } - private void addFolderToMediaStore(File visibleFolder) { - assert(visibleFolder.isDirectory()); + private void addFolderToMediaStore(@Nullable File visibleFolder) { + // visibleFolder is null if we're adding a folder to external thumb drive or SD card. + if (visibleFolder != null) { + assert (visibleFolder.isDirectory()); - final ContentResolver resolver = getContext().getContentResolver(); - final Uri uri = MediaStore.Files.getDirectoryUri("external"); - ContentValues values = new ContentValues(); - values.put(MediaStore.Files.FileColumns.DATA, visibleFolder.getAbsolutePath()); - resolver.insert(uri, values); + final ContentResolver resolver = getContext().getContentResolver(); + final Uri uri = MediaStore.Files.getDirectoryUri("external"); + ContentValues values = new ContentValues(); + values.put(MediaStore.Files.FileColumns.DATA, visibleFolder.getAbsolutePath()); + resolver.insert(uri, values); + } } @Override @@ -204,8 +208,12 @@ public abstract class FileSystemProvider extends DocumentsProvider { return docId; } - private void moveInMediaStore(File oldVisibleFile, File newVisibleFile) { - if (newVisibleFile != null) { + private void moveInMediaStore(@Nullable File oldVisibleFile, @Nullable File newVisibleFile) { + // visibleFolders are null if we're moving a document in external thumb drive or SD card. + // + // They should be all null or not null at the same time. File#renameTo() doesn't work across + // volumes so an exception will be thrown before calling this method. + if (oldVisibleFile != null && newVisibleFile != null) { final ContentResolver resolver = getContext().getContentResolver(); final Uri externalUri = newVisibleFile.isDirectory() ? MediaStore.Files.getDirectoryUri("external") @@ -241,8 +249,9 @@ public abstract class FileSystemProvider extends DocumentsProvider { removeFromMediaStore(visibleFile, isDirectory); } - private void removeFromMediaStore(File visibleFile, boolean isFolder) + private void removeFromMediaStore(@Nullable File visibleFile, boolean isFolder) throws FileNotFoundException { + // visibleFolder is null if we're removing a document from external thumb drive or SD card. if (visibleFile != null) { final ContentResolver resolver = getContext().getContentResolver(); final Uri externalUri = MediaStore.Files.getContentUri("external"); diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java new file mode 100644 index 000000000000..6c1efa43ac86 --- /dev/null +++ b/core/java/com/android/internal/graphics/ColorUtils.java @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.graphics; + +import android.annotation.ColorInt; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.graphics.Color; + +/** + * Copied from: frameworks/support/core-utils/java/android/support/v4/graphics/ColorUtils.java + * + * A set of color-related utility methods, building upon those available in {@code Color}. + */ +public final class ColorUtils { + + private static final double XYZ_WHITE_REFERENCE_X = 95.047; + private static final double XYZ_WHITE_REFERENCE_Y = 100; + private static final double XYZ_WHITE_REFERENCE_Z = 108.883; + private static final double XYZ_EPSILON = 0.008856; + private static final double XYZ_KAPPA = 903.3; + + private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10; + private static final int MIN_ALPHA_SEARCH_PRECISION = 1; + + private static final ThreadLocal<double[]> TEMP_ARRAY = new ThreadLocal<>(); + + private ColorUtils() {} + + /** + * Composite two potentially translucent colors over each other and returns the result. + */ + public static int compositeColors(@ColorInt int foreground, @ColorInt int background) { + int bgAlpha = Color.alpha(background); + int fgAlpha = Color.alpha(foreground); + int a = compositeAlpha(fgAlpha, bgAlpha); + + int r = compositeComponent(Color.red(foreground), fgAlpha, + Color.red(background), bgAlpha, a); + int g = compositeComponent(Color.green(foreground), fgAlpha, + Color.green(background), bgAlpha, a); + int b = compositeComponent(Color.blue(foreground), fgAlpha, + Color.blue(background), bgAlpha, a); + + return Color.argb(a, r, g, b); + } + + private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) { + return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF); + } + + private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) { + if (a == 0) return 0; + return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF); + } + + /** + * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}. + * <p>Defined as the Y component in the XYZ representation of {@code color}.</p> + */ + @FloatRange(from = 0.0, to = 1.0) + public static double calculateLuminance(@ColorInt int color) { + final double[] result = getTempDouble3Array(); + colorToXYZ(color, result); + // Luminance is the Y component + return result[1] / 100; + } + + /** + * Returns the contrast ratio between {@code foreground} and {@code background}. + * {@code background} must be opaque. + * <p> + * Formula defined + * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>. + */ + public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) { + if (Color.alpha(background) != 255) { + throw new IllegalArgumentException("background can not be translucent: #" + + Integer.toHexString(background)); + } + if (Color.alpha(foreground) < 255) { + // If the foreground is translucent, composite the foreground over the background + foreground = compositeColors(foreground, background); + } + + final double luminance1 = calculateLuminance(foreground) + 0.05; + final double luminance2 = calculateLuminance(background) + 0.05; + + // Now return the lighter luminance divided by the darker luminance + return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2); + } + + /** + * Calculates the minimum alpha value which can be applied to {@code foreground} so that would + * have a contrast value of at least {@code minContrastRatio} when compared to + * {@code background}. + * + * @param foreground the foreground color + * @param background the opaque background color + * @param minContrastRatio the minimum contrast ratio + * @return the alpha value in the range 0-255, or -1 if no value could be calculated + */ + public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background, + float minContrastRatio) { + if (Color.alpha(background) != 255) { + throw new IllegalArgumentException("background can not be translucent: #" + + Integer.toHexString(background)); + } + + // First lets check that a fully opaque foreground has sufficient contrast + int testForeground = setAlphaComponent(foreground, 255); + double testRatio = calculateContrast(testForeground, background); + if (testRatio < minContrastRatio) { + // Fully opaque foreground does not have sufficient contrast, return error + return -1; + } + + // Binary search to find a value with the minimum value which provides sufficient contrast + int numIterations = 0; + int minAlpha = 0; + int maxAlpha = 255; + + while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS && + (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) { + final int testAlpha = (minAlpha + maxAlpha) / 2; + + testForeground = setAlphaComponent(foreground, testAlpha); + testRatio = calculateContrast(testForeground, background); + + if (testRatio < minContrastRatio) { + minAlpha = testAlpha; + } else { + maxAlpha = testAlpha; + } + + numIterations++; + } + + // Conservatively return the max of the range of possible alphas, which is known to pass. + return maxAlpha; + } + + /** + * Convert RGB components to HSL (hue-saturation-lightness). + * <ul> + * <li>outHsl[0] is Hue [0 .. 360)</li> + * <li>outHsl[1] is Saturation [0...1]</li> + * <li>outHsl[2] is Lightness [0...1]</li> + * </ul> + * + * @param r red component value [0..255] + * @param g green component value [0..255] + * @param b blue component value [0..255] + * @param outHsl 3-element array which holds the resulting HSL components + */ + public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r, + @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b, + @NonNull float[] outHsl) { + final float rf = r / 255f; + final float gf = g / 255f; + final float bf = b / 255f; + + final float max = Math.max(rf, Math.max(gf, bf)); + final float min = Math.min(rf, Math.min(gf, bf)); + final float deltaMaxMin = max - min; + + float h, s; + float l = (max + min) / 2f; + + if (max == min) { + // Monochromatic + h = s = 0f; + } else { + if (max == rf) { + h = ((gf - bf) / deltaMaxMin) % 6f; + } else if (max == gf) { + h = ((bf - rf) / deltaMaxMin) + 2f; + } else { + h = ((rf - gf) / deltaMaxMin) + 4f; + } + + s = deltaMaxMin / (1f - Math.abs(2f * l - 1f)); + } + + h = (h * 60f) % 360f; + if (h < 0) { + h += 360f; + } + + outHsl[0] = constrain(h, 0f, 360f); + outHsl[1] = constrain(s, 0f, 1f); + outHsl[2] = constrain(l, 0f, 1f); + } + + /** + * Convert the ARGB color to its HSL (hue-saturation-lightness) components. + * <ul> + * <li>outHsl[0] is Hue [0 .. 360)</li> + * <li>outHsl[1] is Saturation [0...1]</li> + * <li>outHsl[2] is Lightness [0...1]</li> + * </ul> + * + * @param color the ARGB color to convert. The alpha component is ignored + * @param outHsl 3-element array which holds the resulting HSL components + */ + public static void colorToHSL(@ColorInt int color, @NonNull float[] outHsl) { + RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), outHsl); + } + + /** + * Convert HSL (hue-saturation-lightness) components to a RGB color. + * <ul> + * <li>hsl[0] is Hue [0 .. 360)</li> + * <li>hsl[1] is Saturation [0...1]</li> + * <li>hsl[2] is Lightness [0...1]</li> + * </ul> + * If hsv values are out of range, they are pinned. + * + * @param hsl 3-element array which holds the input HSL components + * @return the resulting RGB color + */ + @ColorInt + public static int HSLToColor(@NonNull float[] hsl) { + final float h = hsl[0]; + final float s = hsl[1]; + final float l = hsl[2]; + + final float c = (1f - Math.abs(2 * l - 1f)) * s; + final float m = l - 0.5f * c; + final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f)); + + final int hueSegment = (int) h / 60; + + int r = 0, g = 0, b = 0; + + switch (hueSegment) { + case 0: + r = Math.round(255 * (c + m)); + g = Math.round(255 * (x + m)); + b = Math.round(255 * m); + break; + case 1: + r = Math.round(255 * (x + m)); + g = Math.round(255 * (c + m)); + b = Math.round(255 * m); + break; + case 2: + r = Math.round(255 * m); + g = Math.round(255 * (c + m)); + b = Math.round(255 * (x + m)); + break; + case 3: + r = Math.round(255 * m); + g = Math.round(255 * (x + m)); + b = Math.round(255 * (c + m)); + break; + case 4: + r = Math.round(255 * (x + m)); + g = Math.round(255 * m); + b = Math.round(255 * (c + m)); + break; + case 5: + case 6: + r = Math.round(255 * (c + m)); + g = Math.round(255 * m); + b = Math.round(255 * (x + m)); + break; + } + + r = constrain(r, 0, 255); + g = constrain(g, 0, 255); + b = constrain(b, 0, 255); + + return Color.rgb(r, g, b); + } + + /** + * Set the alpha component of {@code color} to be {@code alpha}. + */ + @ColorInt + public static int setAlphaComponent(@ColorInt int color, + @IntRange(from = 0x0, to = 0xFF) int alpha) { + if (alpha < 0 || alpha > 255) { + throw new IllegalArgumentException("alpha must be between 0 and 255."); + } + return (color & 0x00ffffff) | (alpha << 24); + } + + /** + * Convert the ARGB color to its CIE Lab representative components. + * + * @param color the ARGB color to convert. The alpha component is ignored + * @param outLab 3-element array which holds the resulting LAB components + */ + public static void colorToLAB(@ColorInt int color, @NonNull double[] outLab) { + RGBToLAB(Color.red(color), Color.green(color), Color.blue(color), outLab); + } + + /** + * Convert RGB components to its CIE Lab representative components. + * + * <ul> + * <li>outLab[0] is L [0 ...1)</li> + * <li>outLab[1] is a [-128...127)</li> + * <li>outLab[2] is b [-128...127)</li> + * </ul> + * + * @param r red component value [0..255] + * @param g green component value [0..255] + * @param b blue component value [0..255] + * @param outLab 3-element array which holds the resulting LAB components + */ + public static void RGBToLAB(@IntRange(from = 0x0, to = 0xFF) int r, + @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b, + @NonNull double[] outLab) { + // First we convert RGB to XYZ + RGBToXYZ(r, g, b, outLab); + // outLab now contains XYZ + XYZToLAB(outLab[0], outLab[1], outLab[2], outLab); + // outLab now contains LAB representation + } + + /** + * Convert the ARGB color to its CIE XYZ representative components. + * + * <p>The resulting XYZ representation will use the D65 illuminant and the CIE + * 2° Standard Observer (1931).</p> + * + * <ul> + * <li>outXyz[0] is X [0 ...95.047)</li> + * <li>outXyz[1] is Y [0...100)</li> + * <li>outXyz[2] is Z [0...108.883)</li> + * </ul> + * + * @param color the ARGB color to convert. The alpha component is ignored + * @param outXyz 3-element array which holds the resulting LAB components + */ + public static void colorToXYZ(@ColorInt int color, @NonNull double[] outXyz) { + RGBToXYZ(Color.red(color), Color.green(color), Color.blue(color), outXyz); + } + + /** + * Convert RGB components to its CIE XYZ representative components. + * + * <p>The resulting XYZ representation will use the D65 illuminant and the CIE + * 2° Standard Observer (1931).</p> + * + * <ul> + * <li>outXyz[0] is X [0 ...95.047)</li> + * <li>outXyz[1] is Y [0...100)</li> + * <li>outXyz[2] is Z [0...108.883)</li> + * </ul> + * + * @param r red component value [0..255] + * @param g green component value [0..255] + * @param b blue component value [0..255] + * @param outXyz 3-element array which holds the resulting XYZ components + */ + public static void RGBToXYZ(@IntRange(from = 0x0, to = 0xFF) int r, + @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b, + @NonNull double[] outXyz) { + if (outXyz.length != 3) { + throw new IllegalArgumentException("outXyz must have a length of 3."); + } + + double sr = r / 255.0; + sr = sr < 0.04045 ? sr / 12.92 : Math.pow((sr + 0.055) / 1.055, 2.4); + double sg = g / 255.0; + sg = sg < 0.04045 ? sg / 12.92 : Math.pow((sg + 0.055) / 1.055, 2.4); + double sb = b / 255.0; + sb = sb < 0.04045 ? sb / 12.92 : Math.pow((sb + 0.055) / 1.055, 2.4); + + outXyz[0] = 100 * (sr * 0.4124 + sg * 0.3576 + sb * 0.1805); + outXyz[1] = 100 * (sr * 0.2126 + sg * 0.7152 + sb * 0.0722); + outXyz[2] = 100 * (sr * 0.0193 + sg * 0.1192 + sb * 0.9505); + } + + /** + * Converts a color from CIE XYZ to CIE Lab representation. + * + * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE + * 2° Standard Observer (1931).</p> + * + * <ul> + * <li>outLab[0] is L [0 ...1)</li> + * <li>outLab[1] is a [-128...127)</li> + * <li>outLab[2] is b [-128...127)</li> + * </ul> + * + * @param x X component value [0...95.047) + * @param y Y component value [0...100) + * @param z Z component value [0...108.883) + * @param outLab 3-element array which holds the resulting Lab components + */ + public static void XYZToLAB(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x, + @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y, + @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z, + @NonNull double[] outLab) { + if (outLab.length != 3) { + throw new IllegalArgumentException("outLab must have a length of 3."); + } + x = pivotXyzComponent(x / XYZ_WHITE_REFERENCE_X); + y = pivotXyzComponent(y / XYZ_WHITE_REFERENCE_Y); + z = pivotXyzComponent(z / XYZ_WHITE_REFERENCE_Z); + outLab[0] = Math.max(0, 116 * y - 16); + outLab[1] = 500 * (x - y); + outLab[2] = 200 * (y - z); + } + + /** + * Converts a color from CIE Lab to CIE XYZ representation. + * + * <p>The resulting XYZ representation will use the D65 illuminant and the CIE + * 2° Standard Observer (1931).</p> + * + * <ul> + * <li>outXyz[0] is X [0 ...95.047)</li> + * <li>outXyz[1] is Y [0...100)</li> + * <li>outXyz[2] is Z [0...108.883)</li> + * </ul> + * + * @param l L component value [0...100) + * @param a A component value [-128...127) + * @param b B component value [-128...127) + * @param outXyz 3-element array which holds the resulting XYZ components + */ + public static void LABToXYZ(@FloatRange(from = 0f, to = 100) final double l, + @FloatRange(from = -128, to = 127) final double a, + @FloatRange(from = -128, to = 127) final double b, + @NonNull double[] outXyz) { + final double fy = (l + 16) / 116; + final double fx = a / 500 + fy; + final double fz = fy - b / 200; + + double tmp = Math.pow(fx, 3); + final double xr = tmp > XYZ_EPSILON ? tmp : (116 * fx - 16) / XYZ_KAPPA; + final double yr = l > XYZ_KAPPA * XYZ_EPSILON ? Math.pow(fy, 3) : l / XYZ_KAPPA; + + tmp = Math.pow(fz, 3); + final double zr = tmp > XYZ_EPSILON ? tmp : (116 * fz - 16) / XYZ_KAPPA; + + outXyz[0] = xr * XYZ_WHITE_REFERENCE_X; + outXyz[1] = yr * XYZ_WHITE_REFERENCE_Y; + outXyz[2] = zr * XYZ_WHITE_REFERENCE_Z; + } + + /** + * Converts a color from CIE XYZ to its RGB representation. + * + * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE + * 2° Standard Observer (1931).</p> + * + * @param x X component value [0...95.047) + * @param y Y component value [0...100) + * @param z Z component value [0...108.883) + * @return int containing the RGB representation + */ + @ColorInt + public static int XYZToColor(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x, + @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y, + @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z) { + double r = (x * 3.2406 + y * -1.5372 + z * -0.4986) / 100; + double g = (x * -0.9689 + y * 1.8758 + z * 0.0415) / 100; + double b = (x * 0.0557 + y * -0.2040 + z * 1.0570) / 100; + + r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r; + g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g; + b = b > 0.0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - 0.055 : 12.92 * b; + + return Color.rgb( + constrain((int) Math.round(r * 255), 0, 255), + constrain((int) Math.round(g * 255), 0, 255), + constrain((int) Math.round(b * 255), 0, 255)); + } + + /** + * Converts a color from CIE Lab to its RGB representation. + * + * @param l L component value [0...100] + * @param a A component value [-128...127] + * @param b B component value [-128...127] + * @return int containing the RGB representation + */ + @ColorInt + public static int LABToColor(@FloatRange(from = 0f, to = 100) final double l, + @FloatRange(from = -128, to = 127) final double a, + @FloatRange(from = -128, to = 127) final double b) { + final double[] result = getTempDouble3Array(); + LABToXYZ(l, a, b, result); + return XYZToColor(result[0], result[1], result[2]); + } + + /** + * Returns the euclidean distance between two LAB colors. + */ + public static double distanceEuclidean(@NonNull double[] labX, @NonNull double[] labY) { + return Math.sqrt(Math.pow(labX[0] - labY[0], 2) + + Math.pow(labX[1] - labY[1], 2) + + Math.pow(labX[2] - labY[2], 2)); + } + + private static float constrain(float amount, float low, float high) { + return amount < low ? low : (amount > high ? high : amount); + } + + private static int constrain(int amount, int low, int high) { + return amount < low ? low : (amount > high ? high : amount); + } + + private static double pivotXyzComponent(double component) { + return component > XYZ_EPSILON + ? Math.pow(component, 1 / 3.0) + : (XYZ_KAPPA * component + 16) / 116; + } + + /** + * Blend between two ARGB colors using the given ratio. + * + * <p>A blend ratio of 0.0 will result in {@code color1}, 0.5 will give an even blend, + * 1.0 will result in {@code color2}.</p> + * + * @param color1 the first ARGB color + * @param color2 the second ARGB color + * @param ratio the blend ratio of {@code color1} to {@code color2} + */ + @ColorInt + public static int blendARGB(@ColorInt int color1, @ColorInt int color2, + @FloatRange(from = 0.0, to = 1.0) float ratio) { + final float inverseRatio = 1 - ratio; + float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio; + float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio; + float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio; + float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio; + return Color.argb((int) a, (int) r, (int) g, (int) b); + } + + /** + * Blend between {@code hsl1} and {@code hsl2} using the given ratio. This will interpolate + * the hue using the shortest angle. + * + * <p>A blend ratio of 0.0 will result in {@code hsl1}, 0.5 will give an even blend, + * 1.0 will result in {@code hsl2}.</p> + * + * @param hsl1 3-element array which holds the first HSL color + * @param hsl2 3-element array which holds the second HSL color + * @param ratio the blend ratio of {@code hsl1} to {@code hsl2} + * @param outResult 3-element array which holds the resulting HSL components + */ + public static void blendHSL(@NonNull float[] hsl1, @NonNull float[] hsl2, + @FloatRange(from = 0.0, to = 1.0) float ratio, @NonNull float[] outResult) { + if (outResult.length != 3) { + throw new IllegalArgumentException("result must have a length of 3."); + } + final float inverseRatio = 1 - ratio; + // Since hue is circular we will need to interpolate carefully + outResult[0] = circularInterpolate(hsl1[0], hsl2[0], ratio); + outResult[1] = hsl1[1] * inverseRatio + hsl2[1] * ratio; + outResult[2] = hsl1[2] * inverseRatio + hsl2[2] * ratio; + } + + /** + * Blend between two CIE-LAB colors using the given ratio. + * + * <p>A blend ratio of 0.0 will result in {@code lab1}, 0.5 will give an even blend, + * 1.0 will result in {@code lab2}.</p> + * + * @param lab1 3-element array which holds the first LAB color + * @param lab2 3-element array which holds the second LAB color + * @param ratio the blend ratio of {@code lab1} to {@code lab2} + * @param outResult 3-element array which holds the resulting LAB components + */ + public static void blendLAB(@NonNull double[] lab1, @NonNull double[] lab2, + @FloatRange(from = 0.0, to = 1.0) double ratio, @NonNull double[] outResult) { + if (outResult.length != 3) { + throw new IllegalArgumentException("outResult must have a length of 3."); + } + final double inverseRatio = 1 - ratio; + outResult[0] = lab1[0] * inverseRatio + lab2[0] * ratio; + outResult[1] = lab1[1] * inverseRatio + lab2[1] * ratio; + outResult[2] = lab1[2] * inverseRatio + lab2[2] * ratio; + } + + static float circularInterpolate(float a, float b, float f) { + if (Math.abs(b - a) > 180) { + if (b > a) { + a += 360; + } else { + b += 360; + } + } + return (a + ((b - a) * f)) % 360; + } + + private static double[] getTempDouble3Array() { + double[] result = TEMP_ARRAY.get(); + if (result == null) { + result = new double[3]; + TEMP_ARRAY.set(result); + } + return result; + } + +}
\ No newline at end of file diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java new file mode 100644 index 000000000000..2d0ad660de8a --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.graphics.palette; + +/* + * Copyright 2014 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. + */ + +import android.graphics.Color; +import android.util.TimingLogger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +import com.android.internal.graphics.ColorUtils; +import com.android.internal.graphics.palette.Palette.Swatch; + +/** + * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/ + * graphics/ColorCutQuantizer.java + * + * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct + * colors rather than representation colors. + * + * The color space is represented as a 3-dimensional cube with each dimension being an RGB + * component. The cube is then repeatedly divided until we have reduced the color space to the + * requested number of colors. An average color is then generated from each cube. + * + * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes + * have roughly the same population, where this quantizer divides boxes based on their color volume. + * This means that the color space is divided into distinct colors, rather than representative + * colors. + */ +final class ColorCutQuantizer { + + private static final String LOG_TAG = "ColorCutQuantizer"; + private static final boolean LOG_TIMINGS = false; + + static final int COMPONENT_RED = -3; + static final int COMPONENT_GREEN = -2; + static final int COMPONENT_BLUE = -1; + + private static final int QUANTIZE_WORD_WIDTH = 5; + private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1; + + final int[] mColors; + final int[] mHistogram; + final List<Swatch> mQuantizedColors; + final TimingLogger mTimingLogger; + final Palette.Filter[] mFilters; + + private final float[] mTempHsl = new float[3]; + + /** + * Constructor. + * + * @param pixels histogram representing an image's pixel data + * @param maxColors The maximum number of colors that should be in the result palette. + * @param filters Set of filters to use in the quantization stage + */ + ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) { + mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; + mFilters = filters; + + final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; + for (int i = 0; i < pixels.length; i++) { + final int quantizedColor = quantizeFromRgb888(pixels[i]); + // Now update the pixel value to the quantized value + pixels[i] = quantizedColor; + // And update the histogram + hist[quantizedColor]++; + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Histogram created"); + } + + // Now let's count the number of distinct colors + int distinctColorCount = 0; + for (int color = 0; color < hist.length; color++) { + if (hist[color] > 0 && shouldIgnoreColor(color)) { + // If we should ignore the color, set the population to 0 + hist[color] = 0; + } + if (hist[color] > 0) { + // If the color has population, increase the distinct color count + distinctColorCount++; + } + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Filtered colors and distinct colors counted"); + } + + // Now lets go through create an array consisting of only distinct colors + final int[] colors = mColors = new int[distinctColorCount]; + int distinctColorIndex = 0; + for (int color = 0; color < hist.length; color++) { + if (hist[color] > 0) { + colors[distinctColorIndex++] = color; + } + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Distinct colors copied into array"); + } + + if (distinctColorCount <= maxColors) { + // The image has fewer colors than the maximum requested, so just return the colors + mQuantizedColors = new ArrayList<>(); + for (int color : colors) { + mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color])); + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Too few colors present. Copied to Swatches"); + mTimingLogger.dumpToLog(); + } + } else { + // We need use quantization to reduce the number of colors + mQuantizedColors = quantizePixels(maxColors); + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Quantized colors computed"); + mTimingLogger.dumpToLog(); + } + } + } + + /** + * @return the list of quantized colors + */ + List<Swatch> getQuantizedColors() { + return mQuantizedColors; + } + + private List<Swatch> quantizePixels(int maxColors) { + // Create the priority queue which is sorted by volume descending. This means we always + // split the largest box in the queue + final PriorityQueue<Vbox> pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME); + + // To start, offer a box which contains all of the colors + pq.offer(new Vbox(0, mColors.length - 1)); + + // Now go through the boxes, splitting them until we have reached maxColors or there are no + // more boxes to split + splitBoxes(pq, maxColors); + + // Finally, return the average colors of the color boxes + return generateAverageColors(pq); + } + + /** + * Iterate through the {@link java.util.Queue}, popping + * {@link ColorCutQuantizer.Vbox} objects from the queue + * and splitting them. Once split, the new box and the remaining box are offered back to the + * queue. + * + * @param queue {@link java.util.PriorityQueue} to poll for boxes + * @param maxSize Maximum amount of boxes to split + */ + private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) { + while (queue.size() < maxSize) { + final Vbox vbox = queue.poll(); + + if (vbox != null && vbox.canSplit()) { + // First split the box, and offer the result + queue.offer(vbox.splitBox()); + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Box split"); + } + // Then offer the box back + queue.offer(vbox); + } else { + if (LOG_TIMINGS) { + mTimingLogger.addSplit("All boxes split"); + } + // If we get here then there are no more boxes to split, so return + return; + } + } + } + + private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) { + ArrayList<Swatch> colors = new ArrayList<>(vboxes.size()); + for (Vbox vbox : vboxes) { + Swatch swatch = vbox.getAverageColor(); + if (!shouldIgnoreColor(swatch)) { + // As we're averaging a color box, we can still get colors which we do not want, so + // we check again here + colors.add(swatch); + } + } + return colors; + } + + /** + * Represents a tightly fitting box around a color space. + */ + private class Vbox { + // lower and upper index are inclusive + private int mLowerIndex; + private int mUpperIndex; + // Population of colors within this box + private int mPopulation; + + private int mMinRed, mMaxRed; + private int mMinGreen, mMaxGreen; + private int mMinBlue, mMaxBlue; + + Vbox(int lowerIndex, int upperIndex) { + mLowerIndex = lowerIndex; + mUpperIndex = upperIndex; + fitBox(); + } + + final int getVolume() { + return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) * + (mMaxBlue - mMinBlue + 1); + } + + final boolean canSplit() { + return getColorCount() > 1; + } + + final int getColorCount() { + return 1 + mUpperIndex - mLowerIndex; + } + + /** + * Recomputes the boundaries of this box to tightly fit the colors within the box. + */ + final void fitBox() { + final int[] colors = mColors; + final int[] hist = mHistogram; + + // Reset the min and max to opposite values + int minRed, minGreen, minBlue; + minRed = minGreen = minBlue = Integer.MAX_VALUE; + int maxRed, maxGreen, maxBlue; + maxRed = maxGreen = maxBlue = Integer.MIN_VALUE; + int count = 0; + + for (int i = mLowerIndex; i <= mUpperIndex; i++) { + final int color = colors[i]; + count += hist[color]; + + final int r = quantizedRed(color); + final int g = quantizedGreen(color); + final int b = quantizedBlue(color); + if (r > maxRed) { + maxRed = r; + } + if (r < minRed) { + minRed = r; + } + if (g > maxGreen) { + maxGreen = g; + } + if (g < minGreen) { + minGreen = g; + } + if (b > maxBlue) { + maxBlue = b; + } + if (b < minBlue) { + minBlue = b; + } + } + + mMinRed = minRed; + mMaxRed = maxRed; + mMinGreen = minGreen; + mMaxGreen = maxGreen; + mMinBlue = minBlue; + mMaxBlue = maxBlue; + mPopulation = count; + } + + /** + * Split this color box at the mid-point along its longest dimension + * + * @return the new ColorBox + */ + final Vbox splitBox() { + if (!canSplit()) { + throw new IllegalStateException("Can not split a box with only 1 color"); + } + + // find median along the longest dimension + final int splitPoint = findSplitPoint(); + + Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex); + + // Now change this box's upperIndex and recompute the color boundaries + mUpperIndex = splitPoint; + fitBox(); + + return newBox; + } + + /** + * @return the dimension which this box is largest in + */ + final int getLongestColorDimension() { + final int redLength = mMaxRed - mMinRed; + final int greenLength = mMaxGreen - mMinGreen; + final int blueLength = mMaxBlue - mMinBlue; + + if (redLength >= greenLength && redLength >= blueLength) { + return COMPONENT_RED; + } else if (greenLength >= redLength && greenLength >= blueLength) { + return COMPONENT_GREEN; + } else { + return COMPONENT_BLUE; + } + } + + /** + * Finds the point within this box's lowerIndex and upperIndex index of where to split. + * + * This is calculated by finding the longest color dimension, and then sorting the + * sub-array based on that dimension value in each color. The colors are then iterated over + * until a color is found with at least the midpoint of the whole box's dimension midpoint. + * + * @return the index of the colors array to split from + */ + final int findSplitPoint() { + final int longestDimension = getLongestColorDimension(); + final int[] colors = mColors; + final int[] hist = mHistogram; + + // We need to sort the colors in this box based on the longest color dimension. + // As we can't use a Comparator to define the sort logic, we modify each color so that + // its most significant is the desired dimension + modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); + + // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1 + Arrays.sort(colors, mLowerIndex, mUpperIndex + 1); + + // Now revert all of the colors so that they are packed as RGB again + modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); + + final int midPoint = mPopulation / 2; + for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { + count += hist[colors[i]]; + if (count >= midPoint) { + return i; + } + } + + return mLowerIndex; + } + + /** + * @return the average color of this box. + */ + final Swatch getAverageColor() { + final int[] colors = mColors; + final int[] hist = mHistogram; + int redSum = 0; + int greenSum = 0; + int blueSum = 0; + int totalPopulation = 0; + + for (int i = mLowerIndex; i <= mUpperIndex; i++) { + final int color = colors[i]; + final int colorPopulation = hist[color]; + + totalPopulation += colorPopulation; + redSum += colorPopulation * quantizedRed(color); + greenSum += colorPopulation * quantizedGreen(color); + blueSum += colorPopulation * quantizedBlue(color); + } + + final int redMean = Math.round(redSum / (float) totalPopulation); + final int greenMean = Math.round(greenSum / (float) totalPopulation); + final int blueMean = Math.round(blueSum / (float) totalPopulation); + + return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation); + } + } + + /** + * Modify the significant octet in a packed color int. Allows sorting based on the value of a + * single color component. This relies on all components being the same word size. + * + * @see Vbox#findSplitPoint() + */ + static void modifySignificantOctet(final int[] a, final int dimension, + final int lower, final int upper) { + switch (dimension) { + case COMPONENT_RED: + // Already in RGB, no need to do anything + break; + case COMPONENT_GREEN: + // We need to do a RGB to GRB swap, or vice-versa + for (int i = lower; i <= upper; i++) { + final int color = a[i]; + a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) + | quantizedRed(color) << QUANTIZE_WORD_WIDTH + | quantizedBlue(color); + } + break; + case COMPONENT_BLUE: + // We need to do a RGB to BGR swap, or vice-versa + for (int i = lower; i <= upper; i++) { + final int color = a[i]; + a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) + | quantizedGreen(color) << QUANTIZE_WORD_WIDTH + | quantizedRed(color); + } + break; + } + } + + private boolean shouldIgnoreColor(int color565) { + final int rgb = approximateToRgb888(color565); + ColorUtils.colorToHSL(rgb, mTempHsl); + return shouldIgnoreColor(rgb, mTempHsl); + } + + private boolean shouldIgnoreColor(Swatch color) { + return shouldIgnoreColor(color.getRgb(), color.getHsl()); + } + + private boolean shouldIgnoreColor(int rgb, float[] hsl) { + if (mFilters != null && mFilters.length > 0) { + for (int i = 0, count = mFilters.length; i < count; i++) { + if (!mFilters[i].isAllowed(rgb, hsl)) { + return true; + } + } + } + return false; + } + + /** + * Comparator which sorts {@link Vbox} instances based on their volume, in descending order + */ + private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() { + @Override + public int compare(Vbox lhs, Vbox rhs) { + return rhs.getVolume() - lhs.getVolume(); + } + }; + + /** + * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}. + */ + private static int quantizeFromRgb888(int color) { + int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH); + int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH); + int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH); + return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b; + } + + /** + * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}. + */ + static int approximateToRgb888(int r, int g, int b) { + return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8), + modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8), + modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8)); + } + + private static int approximateToRgb888(int color) { + return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); + } + + /** + * @return red component of the quantized color + */ + static int quantizedRed(int color) { + return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK; + } + + /** + * @return green component of a quantized color + */ + static int quantizedGreen(int color) { + return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK; + } + + /** + * @return blue component of a quantized color + */ + static int quantizedBlue(int color) { + return color & QUANTIZE_WORD_MASK; + } + + private static int modifyWordWidth(int value, int currentWidth, int targetWidth) { + final int newValue; + if (targetWidth > currentWidth) { + // If we're approximating up in word width, we'll shift up + newValue = value << (targetWidth - currentWidth); + } else { + // Else, we will just shift and keep the MSB + newValue = value >> (currentWidth - targetWidth); + } + return newValue & ((1 << targetWidth) - 1); + } + +}
\ No newline at end of file diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java new file mode 100644 index 000000000000..9f1504a0495c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/Palette.java @@ -0,0 +1,990 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.graphics.palette; + +import android.annotation.ColorInt; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Rect; +import android.os.AsyncTask; +import android.util.ArrayMap; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.util.TimingLogger; + +import com.android.internal.graphics.ColorUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + + +/** + * Copied from: /frameworks/support/v7/palette/src/main/java/android/support/v7/ + * graphics/Palette.java + * + * A helper class to extract prominent colors from an image. + * <p> + * A number of colors with different profiles are extracted from the image: + * <ul> + * <li>Vibrant</li> + * <li>Vibrant Dark</li> + * <li>Vibrant Light</li> + * <li>Muted</li> + * <li>Muted Dark</li> + * <li>Muted Light</li> + * </ul> + * These can be retrieved from the appropriate getter method. + * + * <p> + * Instances are created with a {@link Palette.Builder} which supports several options to tweak the + * generated Palette. See that class' documentation for more information. + * <p> + * Generation should always be completed on a background thread, ideally the one in + * which you load your image on. {@link Palette.Builder} supports both synchronous and asynchronous + * generation: + * + * <pre> + * // Synchronous + * Palette p = Palette.from(bitmap).generate(); + * + * // Asynchronous + * Palette.from(bitmap).generate(new PaletteAsyncListener() { + * public void onGenerated(Palette p) { + * // Use generated instance + * } + * }); + * </pre> + */ +public final class Palette { + + /** + * Listener to be used with {@link #generateAsync(Bitmap, Palette.PaletteAsyncListener)} or + * {@link #generateAsync(Bitmap, int, Palette.PaletteAsyncListener)} + */ + public interface PaletteAsyncListener { + + /** + * Called when the {@link Palette} has been generated. + */ + void onGenerated(Palette palette); + } + + static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112; + static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; + + static final float MIN_CONTRAST_TITLE_TEXT = 3.0f; + static final float MIN_CONTRAST_BODY_TEXT = 4.5f; + + static final String LOG_TAG = "Palette"; + static final boolean LOG_TIMINGS = false; + + /** + * Start generating a {@link Palette} with the returned {@link Palette.Builder} instance. + */ + public static Palette.Builder from(Bitmap bitmap) { + return new Palette.Builder(bitmap); + } + + /** + * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches. + * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a + * list of swatches. Will return null if the {@code swatches} is null. + */ + public static Palette from(List<Palette.Swatch> swatches) { + return new Palette.Builder(swatches).generate(); + } + + /** + * @deprecated Use {@link Palette.Builder} to generate the Palette. + */ + @Deprecated + public static Palette generate(Bitmap bitmap) { + return from(bitmap).generate(); + } + + /** + * @deprecated Use {@link Palette.Builder} to generate the Palette. + */ + @Deprecated + public static Palette generate(Bitmap bitmap, int numColors) { + return from(bitmap).maximumColorCount(numColors).generate(); + } + + /** + * @deprecated Use {@link Palette.Builder} to generate the Palette. + */ + @Deprecated + public static AsyncTask<Bitmap, Void, Palette> generateAsync( + Bitmap bitmap, Palette.PaletteAsyncListener listener) { + return from(bitmap).generate(listener); + } + + /** + * @deprecated Use {@link Palette.Builder} to generate the Palette. + */ + @Deprecated + public static AsyncTask<Bitmap, Void, Palette> generateAsync( + final Bitmap bitmap, final int numColors, final Palette.PaletteAsyncListener listener) { + return from(bitmap).maximumColorCount(numColors).generate(listener); + } + + private final List<Palette.Swatch> mSwatches; + private final List<Target> mTargets; + + private final Map<Target, Palette.Swatch> mSelectedSwatches; + private final SparseBooleanArray mUsedColors; + + private final Palette.Swatch mDominantSwatch; + + Palette(List<Palette.Swatch> swatches, List<Target> targets) { + mSwatches = swatches; + mTargets = targets; + + mUsedColors = new SparseBooleanArray(); + mSelectedSwatches = new ArrayMap<>(); + + mDominantSwatch = findDominantSwatch(); + } + + /** + * Returns all of the swatches which make up the palette. + */ + @NonNull + public List<Palette.Swatch> getSwatches() { + return Collections.unmodifiableList(mSwatches); + } + + /** + * Returns the targets used to generate this palette. + */ + @NonNull + public List<Target> getTargets() { + return Collections.unmodifiableList(mTargets); + } + + /** + * Returns the most vibrant swatch in the palette. Might be null. + * + * @see Target#VIBRANT + */ + @Nullable + public Palette.Swatch getVibrantSwatch() { + return getSwatchForTarget(Target.VIBRANT); + } + + /** + * Returns a light and vibrant swatch from the palette. Might be null. + * + * @see Target#LIGHT_VIBRANT + */ + @Nullable + public Palette.Swatch getLightVibrantSwatch() { + return getSwatchForTarget(Target.LIGHT_VIBRANT); + } + + /** + * Returns a dark and vibrant swatch from the palette. Might be null. + * + * @see Target#DARK_VIBRANT + */ + @Nullable + public Palette.Swatch getDarkVibrantSwatch() { + return getSwatchForTarget(Target.DARK_VIBRANT); + } + + /** + * Returns a muted swatch from the palette. Might be null. + * + * @see Target#MUTED + */ + @Nullable + public Palette.Swatch getMutedSwatch() { + return getSwatchForTarget(Target.MUTED); + } + + /** + * Returns a muted and light swatch from the palette. Might be null. + * + * @see Target#LIGHT_MUTED + */ + @Nullable + public Palette.Swatch getLightMutedSwatch() { + return getSwatchForTarget(Target.LIGHT_MUTED); + } + + /** + * Returns a muted and dark swatch from the palette. Might be null. + * + * @see Target#DARK_MUTED + */ + @Nullable + public Palette.Swatch getDarkMutedSwatch() { + return getSwatchForTarget(Target.DARK_MUTED); + } + + /** + * Returns the most vibrant color in the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getVibrantSwatch() + */ + @ColorInt + public int getVibrantColor(@ColorInt final int defaultColor) { + return getColorForTarget(Target.VIBRANT, defaultColor); + } + + /** + * Returns a light and vibrant color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getLightVibrantSwatch() + */ + @ColorInt + public int getLightVibrantColor(@ColorInt final int defaultColor) { + return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor); + } + + /** + * Returns a dark and vibrant color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getDarkVibrantSwatch() + */ + @ColorInt + public int getDarkVibrantColor(@ColorInt final int defaultColor) { + return getColorForTarget(Target.DARK_VIBRANT, defaultColor); + } + + /** + * Returns a muted color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getMutedSwatch() + */ + @ColorInt + public int getMutedColor(@ColorInt final int defaultColor) { + return getColorForTarget(Target.MUTED, defaultColor); + } + + /** + * Returns a muted and light color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getLightMutedSwatch() + */ + @ColorInt + public int getLightMutedColor(@ColorInt final int defaultColor) { + return getColorForTarget(Target.LIGHT_MUTED, defaultColor); + } + + /** + * Returns a muted and dark color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getDarkMutedSwatch() + */ + @ColorInt + public int getDarkMutedColor(@ColorInt final int defaultColor) { + return getColorForTarget(Target.DARK_MUTED, defaultColor); + } + + /** + * Returns the selected swatch for the given target from the palette, or {@code null} if one + * could not be found. + */ + @Nullable + public Palette.Swatch getSwatchForTarget(@NonNull final Target target) { + return mSelectedSwatches.get(target); + } + + /** + * Returns the selected color for the given target from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) { + Palette.Swatch swatch = getSwatchForTarget(target); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns the dominant swatch from the palette. + * + * <p>The dominant swatch is defined as the swatch with the greatest population (frequency) + * within the palette.</p> + */ + @Nullable + public Palette.Swatch getDominantSwatch() { + return mDominantSwatch; + } + + /** + * Returns the color of the dominant swatch from the palette, as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + * @see #getDominantSwatch() + */ + @ColorInt + public int getDominantColor(@ColorInt int defaultColor) { + return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor; + } + + void generate() { + // We need to make sure that the scored targets are generated first. This is so that + // inherited targets have something to inherit from + for (int i = 0, count = mTargets.size(); i < count; i++) { + final Target target = mTargets.get(i); + target.normalizeWeights(); + mSelectedSwatches.put(target, generateScoredTarget(target)); + } + // We now clear out the used colors + mUsedColors.clear(); + } + + private Palette.Swatch generateScoredTarget(final Target target) { + final Palette.Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target); + if (maxScoreSwatch != null && target.isExclusive()) { + // If we have a swatch, and the target is exclusive, add the color to the used list + mUsedColors.append(maxScoreSwatch.getRgb(), true); + } + return maxScoreSwatch; + } + + private Palette.Swatch getMaxScoredSwatchForTarget(final Target target) { + float maxScore = 0; + Palette.Swatch maxScoreSwatch = null; + for (int i = 0, count = mSwatches.size(); i < count; i++) { + final Palette.Swatch swatch = mSwatches.get(i); + if (shouldBeScoredForTarget(swatch, target)) { + final float score = generateScore(swatch, target); + if (maxScoreSwatch == null || score > maxScore) { + maxScoreSwatch = swatch; + maxScore = score; + } + } + } + return maxScoreSwatch; + } + + private boolean shouldBeScoredForTarget(final Palette.Swatch swatch, final Target target) { + // Check whether the HSL values are within the correct ranges, and this color hasn't + // been used yet. + final float hsl[] = swatch.getHsl(); + return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation() + && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness() + && !mUsedColors.get(swatch.getRgb()); + } + + private float generateScore(Palette.Swatch swatch, Target target) { + final float[] hsl = swatch.getHsl(); + + float saturationScore = 0; + float luminanceScore = 0; + float populationScore = 0; + + final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1; + + if (target.getSaturationWeight() > 0) { + saturationScore = target.getSaturationWeight() + * (1f - Math.abs(hsl[1] - target.getTargetSaturation())); + } + if (target.getLightnessWeight() > 0) { + luminanceScore = target.getLightnessWeight() + * (1f - Math.abs(hsl[2] - target.getTargetLightness())); + } + if (target.getPopulationWeight() > 0) { + populationScore = target.getPopulationWeight() + * (swatch.getPopulation() / (float) maxPopulation); + } + + return saturationScore + luminanceScore + populationScore; + } + + private Palette.Swatch findDominantSwatch() { + int maxPop = Integer.MIN_VALUE; + Palette.Swatch maxSwatch = null; + for (int i = 0, count = mSwatches.size(); i < count; i++) { + Palette.Swatch swatch = mSwatches.get(i); + if (swatch.getPopulation() > maxPop) { + maxSwatch = swatch; + maxPop = swatch.getPopulation(); + } + } + return maxSwatch; + } + + private static float[] copyHslValues(Palette.Swatch color) { + final float[] newHsl = new float[3]; + System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); + return newHsl; + } + + /** + * Represents a color swatch generated from an image's palette. The RGB color can be retrieved + * by calling {@link #getRgb()}. + */ + public static final class Swatch { + private final int mRed, mGreen, mBlue; + private final int mRgb; + private final int mPopulation; + + private boolean mGeneratedTextColors; + private int mTitleTextColor; + private int mBodyTextColor; + + private float[] mHsl; + + public Swatch(@ColorInt int color, int population) { + mRed = Color.red(color); + mGreen = Color.green(color); + mBlue = Color.blue(color); + mRgb = color; + mPopulation = population; + } + + Swatch(int red, int green, int blue, int population) { + mRed = red; + mGreen = green; + mBlue = blue; + mRgb = Color.rgb(red, green, blue); + mPopulation = population; + } + + Swatch(float[] hsl, int population) { + this(ColorUtils.HSLToColor(hsl), population); + mHsl = hsl; + } + + /** + * @return this swatch's RGB color value + */ + @ColorInt + public int getRgb() { + return mRgb; + } + + /** + * Return this swatch's HSL values. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Lightness [0...1] + */ + public float[] getHsl() { + if (mHsl == null) { + mHsl = new float[3]; + } + ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl); + return mHsl; + } + + /** + * @return the number of pixels represented by this swatch + */ + public int getPopulation() { + return mPopulation; + } + + /** + * Returns an appropriate color to use for any 'title' text which is displayed over this + * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast. + */ + @ColorInt + public int getTitleTextColor() { + ensureTextColorsGenerated(); + return mTitleTextColor; + } + + /** + * Returns an appropriate color to use for any 'body' text which is displayed over this + * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast. + */ + @ColorInt + public int getBodyTextColor() { + ensureTextColorsGenerated(); + return mBodyTextColor; + } + + private void ensureTextColorsGenerated() { + if (!mGeneratedTextColors) { + // First check white, as most colors will be dark + final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha( + Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT); + final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha( + Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT); + + if (lightBodyAlpha != -1 && lightTitleAlpha != -1) { + // If we found valid light values, use them and return + mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha); + mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha); + mGeneratedTextColors = true; + return; + } + + final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha( + Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT); + final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha( + Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT); + + if (darkBodyAlpha != -1 && darkTitleAlpha != -1) { + // If we found valid dark values, use them and return + mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); + mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); + mGeneratedTextColors = true; + return; + } + + // If we reach here then we can not find title and body values which use the same + // lightness, we need to use mismatched values + mBodyTextColor = lightBodyAlpha != -1 + ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha) + : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); + mTitleTextColor = lightTitleAlpha != -1 + ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha) + : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); + mGeneratedTextColors = true; + } + } + + @Override + public String toString() { + return new StringBuilder(getClass().getSimpleName()) + .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']') + .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']') + .append(" [Population: ").append(mPopulation).append(']') + .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor())) + .append(']') + .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor())) + .append(']').toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Palette.Swatch + swatch = (Palette.Swatch) o; + return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb; + } + + @Override + public int hashCode() { + return 31 * mRgb + mPopulation; + } + } + + /** + * Builder class for generating {@link Palette} instances. + */ + public static final class Builder { + private final List<Palette.Swatch> mSwatches; + private final Bitmap mBitmap; + + private final List<Target> mTargets = new ArrayList<>(); + + private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS; + private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA; + private int mResizeMaxDimension = -1; + + private final List<Palette.Filter> mFilters = new ArrayList<>(); + private Rect mRegion; + + /** + * Construct a new {@link Palette.Builder} using a source {@link Bitmap} + */ + public Builder(Bitmap bitmap) { + if (bitmap == null || bitmap.isRecycled()) { + throw new IllegalArgumentException("Bitmap is not valid"); + } + mFilters.add(DEFAULT_FILTER); + mBitmap = bitmap; + mSwatches = null; + + // Add the default targets + mTargets.add(Target.LIGHT_VIBRANT); + mTargets.add(Target.VIBRANT); + mTargets.add(Target.DARK_VIBRANT); + mTargets.add(Target.LIGHT_MUTED); + mTargets.add(Target.MUTED); + mTargets.add(Target.DARK_MUTED); + } + + /** + * Construct a new {@link Palette.Builder} using a list of {@link Palette.Swatch} instances. + * Typically only used for testing. + */ + public Builder(List<Palette.Swatch> swatches) { + if (swatches == null || swatches.isEmpty()) { + throw new IllegalArgumentException("List of Swatches is not valid"); + } + mFilters.add(DEFAULT_FILTER); + mSwatches = swatches; + mBitmap = null; + } + + /** + * Set the maximum number of colors to use in the quantization step when using a + * {@link android.graphics.Bitmap} as the source. + * <p> + * Good values for depend on the source image type. For landscapes, good values are in + * the range 10-16. For images which are largely made up of people's faces then this + * value should be increased to ~24. + */ + @NonNull + public Palette.Builder maximumColorCount(int colors) { + mMaxColors = colors; + return this; + } + + /** + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. + * If the bitmap's largest dimension is greater than the value specified, then the bitmap + * will be resized so that its largest dimension matches {@code maxDimension}. If the + * bitmap is smaller or equal, the original is used as-is. + * + * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle + * abnormal aspect ratios more gracefully. + * + * @param maxDimension the number of pixels that the max dimension should be scaled down to, + * or any value <= 0 to disable resizing. + */ + @NonNull + @Deprecated + public Palette.Builder resizeBitmapSize(final int maxDimension) { + mResizeMaxDimension = maxDimension; + mResizeArea = -1; + return this; + } + + /** + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. + * If the bitmap's area is greater than the value specified, then the bitmap + * will be resized so that its area matches {@code area}. If the + * bitmap is smaller or equal, the original is used as-is. + * <p> + * This value has a large effect on the processing time. The larger the resized image is, + * the greater time it will take to generate the palette. The smaller the image is, the + * more detail is lost in the resulting image and thus less precision for color selection. + * + * @param area the number of pixels that the intermediary scaled down Bitmap should cover, + * or any value <= 0 to disable resizing. + */ + @NonNull + public Palette.Builder resizeBitmapArea(final int area) { + mResizeArea = area; + mResizeMaxDimension = -1; + return this; + } + + /** + * Clear all added filters. This includes any default filters added automatically by + * {@link Palette}. + */ + @NonNull + public Palette.Builder clearFilters() { + mFilters.clear(); + return this; + } + + /** + * Add a filter to be able to have fine grained control over which colors are + * allowed in the resulting palette. + * + * @param filter filter to add. + */ + @NonNull + public Palette.Builder addFilter( + Palette.Filter filter) { + if (filter != null) { + mFilters.add(filter); + } + return this; + } + + /** + * Set a region of the bitmap to be used exclusively when calculating the palette. + * <p>This only works when the original input is a {@link Bitmap}.</p> + * + * @param left The left side of the rectangle used for the region. + * @param top The top of the rectangle used for the region. + * @param right The right side of the rectangle used for the region. + * @param bottom The bottom of the rectangle used for the region. + */ + @NonNull + public Palette.Builder setRegion(int left, int top, int right, int bottom) { + if (mBitmap != null) { + if (mRegion == null) mRegion = new Rect(); + // Set the Rect to be initially the whole Bitmap + mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); + // Now just get the intersection with the region + if (!mRegion.intersect(left, top, right, bottom)) { + throw new IllegalArgumentException("The given region must intersect with " + + "the Bitmap's dimensions."); + } + } + return this; + } + + /** + * Clear any previously region set via {@link #setRegion(int, int, int, int)}. + */ + @NonNull + public Palette.Builder clearRegion() { + mRegion = null; + return this; + } + + /** + * Add a target profile to be generated in the palette. + * + * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p> + */ + @NonNull + public Palette.Builder addTarget(@NonNull final Target target) { + if (!mTargets.contains(target)) { + mTargets.add(target); + } + return this; + } + + /** + * Clear all added targets. This includes any default targets added automatically by + * {@link Palette}. + */ + @NonNull + public Palette.Builder clearTargets() { + if (mTargets != null) { + mTargets.clear(); + } + return this; + } + + /** + * Generate and return the {@link Palette} synchronously. + */ + @NonNull + public Palette generate() { + final TimingLogger logger = LOG_TIMINGS + ? new TimingLogger(LOG_TAG, "Generation") + : null; + + List<Palette.Swatch> swatches; + + if (mBitmap != null) { + // We have a Bitmap so we need to use quantization to reduce the number of colors + + // First we'll scale down the bitmap if needed + final Bitmap bitmap = scaleBitmapDown(mBitmap); + + if (logger != null) { + logger.addSplit("Processed Bitmap"); + } + + final Rect region = mRegion; + if (bitmap != mBitmap && region != null) { + // If we have a scaled bitmap and a selected region, we need to scale down the + // region to match the new scale + final double scale = bitmap.getWidth() / (double) mBitmap.getWidth(); + region.left = (int) Math.floor(region.left * scale); + region.top = (int) Math.floor(region.top * scale); + region.right = Math.min((int) Math.ceil(region.right * scale), + bitmap.getWidth()); + region.bottom = Math.min((int) Math.ceil(region.bottom * scale), + bitmap.getHeight()); + } + + // Now generate a quantizer from the Bitmap + final ColorCutQuantizer quantizer = new ColorCutQuantizer( + getPixelsFromBitmap(bitmap), + mMaxColors, + mFilters.isEmpty() ? null : mFilters.toArray(new Palette.Filter[mFilters.size()])); + + // If created a new bitmap, recycle it + if (bitmap != mBitmap) { + bitmap.recycle(); + } + + swatches = quantizer.getQuantizedColors(); + + if (logger != null) { + logger.addSplit("Color quantization completed"); + } + } else { + // Else we're using the provided swatches + swatches = mSwatches; + } + + // Now create a Palette instance + final Palette p = new Palette(swatches, mTargets); + // And make it generate itself + p.generate(); + + if (logger != null) { + logger.addSplit("Created Palette"); + logger.dumpToLog(); + } + + return p; + } + + /** + * Generate the {@link Palette} asynchronously. The provided listener's + * {@link Palette.PaletteAsyncListener#onGenerated} method will be called with the palette when + * generated. + */ + @NonNull + public AsyncTask<Bitmap, Void, Palette> generate(final Palette.PaletteAsyncListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener can not be null"); + } + + return new AsyncTask<Bitmap, Void, Palette>() { + @Override + protected Palette doInBackground(Bitmap... params) { + try { + return generate(); + } catch (Exception e) { + Log.e(LOG_TAG, "Exception thrown during async generate", e); + return null; + } + } + + @Override + protected void onPostExecute(Palette colorExtractor) { + listener.onGenerated(colorExtractor); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); + } + + private int[] getPixelsFromBitmap(Bitmap bitmap) { + final int bitmapWidth = bitmap.getWidth(); + final int bitmapHeight = bitmap.getHeight(); + final int[] pixels = new int[bitmapWidth * bitmapHeight]; + bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight); + + if (mRegion == null) { + // If we don't have a region, return all of the pixels + return pixels; + } else { + // If we do have a region, lets create a subset array containing only the region's + // pixels + final int regionWidth = mRegion.width(); + final int regionHeight = mRegion.height(); + // pixels contains all of the pixels, so we need to iterate through each row and + // copy the regions pixels into a new smaller array + final int[] subsetPixels = new int[regionWidth * regionHeight]; + for (int row = 0; row < regionHeight; row++) { + System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left, + subsetPixels, row * regionWidth, regionWidth); + } + return subsetPixels; + } + } + + /** + * Scale the bitmap down as needed. + */ + private Bitmap scaleBitmapDown(final Bitmap bitmap) { + double scaleRatio = -1; + + if (mResizeArea > 0) { + final int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); + if (bitmapArea > mResizeArea) { + scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea); + } + } else if (mResizeMaxDimension > 0) { + final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); + if (maxDimension > mResizeMaxDimension) { + scaleRatio = mResizeMaxDimension / (double) maxDimension; + } + } + + if (scaleRatio <= 0) { + // Scaling has been disabled or not needed so just return the Bitmap + return bitmap; + } + + return Bitmap.createScaledBitmap(bitmap, + (int) Math.ceil(bitmap.getWidth() * scaleRatio), + (int) Math.ceil(bitmap.getHeight() * scaleRatio), + false); + } + } + + /** + * A Filter provides a mechanism for exercising fine-grained control over which colors + * are valid within a resulting {@link Palette}. + */ + public interface Filter { + /** + * Hook to allow clients to be able filter colors from resulting palette. + * + * @param rgb the color in RGB888. + * @param hsl HSL representation of the color. + * + * @return true if the color is allowed, false if not. + * + * @see Palette.Builder#addFilter(Palette.Filter) + */ + boolean isAllowed(int rgb, float[] hsl); + } + + /** + * The default filter. + */ + static final Palette.Filter + DEFAULT_FILTER = new Palette.Filter() { + private static final float BLACK_MAX_LIGHTNESS = 0.05f; + private static final float WHITE_MIN_LIGHTNESS = 0.95f; + + @Override + public boolean isAllowed(int rgb, float[] hsl) { + return !isWhite(hsl) && !isBlack(hsl) && !isNearRedILine(hsl); + } + + /** + * @return true if the color represents a color which is close to black. + */ + private boolean isBlack(float[] hslColor) { + return hslColor[2] <= BLACK_MAX_LIGHTNESS; + } + + /** + * @return true if the color represents a color which is close to white. + */ + private boolean isWhite(float[] hslColor) { + return hslColor[2] >= WHITE_MIN_LIGHTNESS; + } + + /** + * @return true if the color lies close to the red side of the I line. + */ + private boolean isNearRedILine(float[] hslColor) { + return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f; + } + }; +} diff --git a/core/java/com/android/internal/graphics/palette/Target.java b/core/java/com/android/internal/graphics/palette/Target.java new file mode 100644 index 000000000000..0540d80ef6f0 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/Target.java @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.graphics.palette; + +/* + * Copyright 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. + */ + +import android.annotation.FloatRange; + +/** + * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/graphics/Target.java + * + * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances + * can be created via the {@link android.support.v7.graphics.Target.Builder} class. + * + * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a + * Palette.</p> + */ +public final class Target { + + private static final float TARGET_DARK_LUMA = 0.26f; + private static final float MAX_DARK_LUMA = 0.45f; + + private static final float MIN_LIGHT_LUMA = 0.55f; + private static final float TARGET_LIGHT_LUMA = 0.74f; + + private static final float MIN_NORMAL_LUMA = 0.3f; + private static final float TARGET_NORMAL_LUMA = 0.5f; + private static final float MAX_NORMAL_LUMA = 0.7f; + + private static final float TARGET_MUTED_SATURATION = 0.3f; + private static final float MAX_MUTED_SATURATION = 0.4f; + + private static final float TARGET_VIBRANT_SATURATION = 1f; + private static final float MIN_VIBRANT_SATURATION = 0.35f; + + private static final float WEIGHT_SATURATION = 0.24f; + private static final float WEIGHT_LUMA = 0.52f; + private static final float WEIGHT_POPULATION = 0.24f; + + static final int INDEX_MIN = 0; + static final int INDEX_TARGET = 1; + static final int INDEX_MAX = 2; + + static final int INDEX_WEIGHT_SAT = 0; + static final int INDEX_WEIGHT_LUMA = 1; + static final int INDEX_WEIGHT_POP = 2; + + /** + * A target which has the characteristics of a vibrant color which is light in luminance. + */ + public static final Target LIGHT_VIBRANT; + + /** + * A target which has the characteristics of a vibrant color which is neither light or dark. + */ + public static final Target VIBRANT; + + /** + * A target which has the characteristics of a vibrant color which is dark in luminance. + */ + public static final Target DARK_VIBRANT; + + /** + * A target which has the characteristics of a muted color which is light in luminance. + */ + public static final Target LIGHT_MUTED; + + /** + * A target which has the characteristics of a muted color which is neither light or dark. + */ + public static final Target MUTED; + + /** + * A target which has the characteristics of a muted color which is dark in luminance. + */ + public static final Target DARK_MUTED; + + static { + LIGHT_VIBRANT = new Target(); + setDefaultLightLightnessValues(LIGHT_VIBRANT); + setDefaultVibrantSaturationValues(LIGHT_VIBRANT); + + VIBRANT = new Target(); + setDefaultNormalLightnessValues(VIBRANT); + setDefaultVibrantSaturationValues(VIBRANT); + + DARK_VIBRANT = new Target(); + setDefaultDarkLightnessValues(DARK_VIBRANT); + setDefaultVibrantSaturationValues(DARK_VIBRANT); + + LIGHT_MUTED = new Target(); + setDefaultLightLightnessValues(LIGHT_MUTED); + setDefaultMutedSaturationValues(LIGHT_MUTED); + + MUTED = new Target(); + setDefaultNormalLightnessValues(MUTED); + setDefaultMutedSaturationValues(MUTED); + + DARK_MUTED = new Target(); + setDefaultDarkLightnessValues(DARK_MUTED); + setDefaultMutedSaturationValues(DARK_MUTED); + } + + final float[] mSaturationTargets = new float[3]; + final float[] mLightnessTargets = new float[3]; + final float[] mWeights = new float[3]; + boolean mIsExclusive = true; // default to true + + Target() { + setTargetDefaultValues(mSaturationTargets); + setTargetDefaultValues(mLightnessTargets); + setDefaultWeights(); + } + + Target(Target from) { + System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0, + mSaturationTargets.length); + System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0, + mLightnessTargets.length); + System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length); + } + + /** + * The minimum saturation value for this target. + */ + @FloatRange(from = 0, to = 1) + public float getMinimumSaturation() { + return mSaturationTargets[INDEX_MIN]; + } + + /** + * The target saturation value for this target. + */ + @FloatRange(from = 0, to = 1) + public float getTargetSaturation() { + return mSaturationTargets[INDEX_TARGET]; + } + + /** + * The maximum saturation value for this target. + */ + @FloatRange(from = 0, to = 1) + public float getMaximumSaturation() { + return mSaturationTargets[INDEX_MAX]; + } + + /** + * The minimum lightness value for this target. + */ + @FloatRange(from = 0, to = 1) + public float getMinimumLightness() { + return mLightnessTargets[INDEX_MIN]; + } + + /** + * The target lightness value for this target. + */ + @FloatRange(from = 0, to = 1) + public float getTargetLightness() { + return mLightnessTargets[INDEX_TARGET]; + } + + /** + * The maximum lightness value for this target. + */ + @FloatRange(from = 0, to = 1) + public float getMaximumLightness() { + return mLightnessTargets[INDEX_MAX]; + } + + /** + * Returns the weight of importance that this target places on a color's saturation within + * the image. + * + * <p>The larger the weight, relative to the other weights, the more important that a color + * being close to the target value has on selection.</p> + * + * @see #getTargetSaturation() + */ + public float getSaturationWeight() { + return mWeights[INDEX_WEIGHT_SAT]; + } + + /** + * Returns the weight of importance that this target places on a color's lightness within + * the image. + * + * <p>The larger the weight, relative to the other weights, the more important that a color + * being close to the target value has on selection.</p> + * + * @see #getTargetLightness() + */ + public float getLightnessWeight() { + return mWeights[INDEX_WEIGHT_LUMA]; + } + + /** + * Returns the weight of importance that this target places on a color's population within + * the image. + * + * <p>The larger the weight, relative to the other weights, the more important that a + * color's population being close to the most populous has on selection.</p> + */ + public float getPopulationWeight() { + return mWeights[INDEX_WEIGHT_POP]; + } + + /** + * Returns whether any color selected for this target is exclusive for this target only. + * + * <p>If false, then the color can be selected for other targets.</p> + */ + public boolean isExclusive() { + return mIsExclusive; + } + + private static void setTargetDefaultValues(final float[] values) { + values[INDEX_MIN] = 0f; + values[INDEX_TARGET] = 0.5f; + values[INDEX_MAX] = 1f; + } + + private void setDefaultWeights() { + mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION; + mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA; + mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION; + } + + void normalizeWeights() { + float sum = 0; + for (int i = 0, z = mWeights.length; i < z; i++) { + float weight = mWeights[i]; + if (weight > 0) { + sum += weight; + } + } + if (sum != 0) { + for (int i = 0, z = mWeights.length; i < z; i++) { + if (mWeights[i] > 0) { + mWeights[i] /= sum; + } + } + } + } + + private static void setDefaultDarkLightnessValues(Target target) { + target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA; + target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA; + } + + private static void setDefaultNormalLightnessValues(Target target) { + target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA; + target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA; + target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA; + } + + private static void setDefaultLightLightnessValues(Target target) { + target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA; + target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA; + } + + private static void setDefaultVibrantSaturationValues(Target target) { + target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION; + target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION; + } + + private static void setDefaultMutedSaturationValues(Target target) { + target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION; + target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION; + } + + /** + * Builder class for generating custom {@link Target} instances. + */ + public final static class Builder { + private final Target mTarget; + + /** + * Create a new {@link Target} builder from scratch. + */ + public Builder() { + mTarget = new Target(); + } + + /** + * Create a new builder based on an existing {@link Target}. + */ + public Builder(Target target) { + mTarget = new Target(target); + } + + /** + * Set the minimum saturation value for this target. + */ + public Target.Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) { + mTarget.mSaturationTargets[INDEX_MIN] = value; + return this; + } + + /** + * Set the target/ideal saturation value for this target. + */ + public Target.Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) { + mTarget.mSaturationTargets[INDEX_TARGET] = value; + return this; + } + + /** + * Set the maximum saturation value for this target. + */ + public Target.Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) { + mTarget.mSaturationTargets[INDEX_MAX] = value; + return this; + } + + /** + * Set the minimum lightness value for this target. + */ + public Target.Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) { + mTarget.mLightnessTargets[INDEX_MIN] = value; + return this; + } + + /** + * Set the target/ideal lightness value for this target. + */ + public Target.Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) { + mTarget.mLightnessTargets[INDEX_TARGET] = value; + return this; + } + + /** + * Set the maximum lightness value for this target. + */ + public Target.Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) { + mTarget.mLightnessTargets[INDEX_MAX] = value; + return this; + } + + /** + * Set the weight of importance that this target will place on saturation values. + * + * <p>The larger the weight, relative to the other weights, the more important that a color + * being close to the target value has on selection.</p> + * + * <p>A weight of 0 means that it has no weight, and thus has no + * bearing on the selection.</p> + * + * @see #setTargetSaturation(float) + */ + public Target.Builder setSaturationWeight(@FloatRange(from = 0) float weight) { + mTarget.mWeights[INDEX_WEIGHT_SAT] = weight; + return this; + } + + /** + * Set the weight of importance that this target will place on lightness values. + * + * <p>The larger the weight, relative to the other weights, the more important that a color + * being close to the target value has on selection.</p> + * + * <p>A weight of 0 means that it has no weight, and thus has no + * bearing on the selection.</p> + * + * @see #setTargetLightness(float) + */ + public Target.Builder setLightnessWeight(@FloatRange(from = 0) float weight) { + mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight; + return this; + } + + /** + * Set the weight of importance that this target will place on a color's population within + * the image. + * + * <p>The larger the weight, relative to the other weights, the more important that a + * color's population being close to the most populous has on selection.</p> + * + * <p>A weight of 0 means that it has no weight, and thus has no + * bearing on the selection.</p> + */ + public Target.Builder setPopulationWeight(@FloatRange(from = 0) float weight) { + mTarget.mWeights[INDEX_WEIGHT_POP] = weight; + return this; + } + + /** + * Set whether any color selected for this target is exclusive to this target only. + * Defaults to true. + * + * @param exclusive true if any the color is exclusive to this target, or false is the + * color can be selected for other targets. + */ + public Target.Builder setExclusive(boolean exclusive) { + mTarget.mIsExclusive = exclusive; + return this; + } + + /** + * Builds and returns the resulting {@link Target}. + */ + public Target build() { + return mTarget; + } + } + +}
\ No newline at end of file diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7fbfb8baedc4..a582c2c330b3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -114,7 +114,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 154 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 155 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -565,9 +565,8 @@ public class BatteryStatsImpl extends BatteryStats { private int mEstimatedBatteryCapacity = -1; - // Last learned capacity reported by BatteryService in - // setBatteryState(). - private int mLastChargeFullUAh = 0; + private int mMinLearnedBatteryCapacity = -1; + private int mMaxLearnedBatteryCapacity = -1; private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry(); @@ -605,6 +604,16 @@ public class BatteryStatsImpl extends BatteryStats { return mEstimatedBatteryCapacity; } + @Override + public int getMinLearnedBatteryCapacity() { + return mMinLearnedBatteryCapacity; + } + + @Override + public int getMaxLearnedBatteryCapacity() { + return mMaxLearnedBatteryCapacity; + } + public BatteryStatsImpl() { this(new SystemClocks()); } @@ -8832,6 +8841,8 @@ public class BatteryStatsImpl extends BatteryStats { } else { mEstimatedBatteryCapacity = -1; } + mMinLearnedBatteryCapacity = -1; + mMaxLearnedBatteryCapacity = -1; mInteractiveTimer.reset(false); mPowerSaveModeEnabledTimer.reset(false); mLastIdleTimeStart = elapsedRealtimeMillis; @@ -10193,15 +10204,12 @@ public class BatteryStatsImpl extends BatteryStats { mRecordingHistory = DEBUG; } - if (differsByMoreThan(chargeFullUAh, mLastChargeFullUAh, 100)) { - mLastChargeFullUAh = chargeFullUAh; - addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ESTIMATED_BATTERY_CAP, - "", chargeFullUAh / 1000); + if (mMinLearnedBatteryCapacity == -1) { + mMinLearnedBatteryCapacity = chargeFullUAh; + } else { + Math.min(mMinLearnedBatteryCapacity, chargeFullUAh); } - } - - private static boolean differsByMoreThan(int left, int right, int diff) { - return Math.abs(left - right) > diff; + mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh); } public long getAwakeTimeBattery() { @@ -10814,6 +10822,8 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeCurrentLevel = in.readInt(); mCurrentBatteryLevel = in.readInt(); mEstimatedBatteryCapacity = in.readInt(); + mMinLearnedBatteryCapacity = in.readInt(); + mMaxLearnedBatteryCapacity = in.readInt(); mLowDischargeAmountSinceCharge = in.readInt(); mHighDischargeAmountSinceCharge = in.readInt(); mDischargeAmountScreenOnSinceCharge = in.readInt(); @@ -11189,6 +11199,8 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(mDischargeCurrentLevel); out.writeInt(mCurrentBatteryLevel); out.writeInt(mEstimatedBatteryCapacity); + out.writeInt(mMinLearnedBatteryCapacity); + out.writeInt(mMaxLearnedBatteryCapacity); out.writeInt(getLowDischargeAmountSinceCharge()); out.writeInt(getHighDischargeAmountSinceCharge()); out.writeInt(getDischargeAmountScreenOnSinceCharge()); @@ -11581,6 +11593,8 @@ public class BatteryStatsImpl extends BatteryStats { mRealtimeStart = in.readLong(); mOnBattery = in.readInt() != 0; mEstimatedBatteryCapacity = in.readInt(); + mMinLearnedBatteryCapacity = in.readInt(); + mMaxLearnedBatteryCapacity = in.readInt(); mOnBatteryInternal = false; // we are no longer really running. mOnBatteryTimeBase.readFromParcel(in); mOnBatteryScreenOffTimeBase.readFromParcel(in); @@ -11775,6 +11789,8 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mRealtimeStart); out.writeInt(mOnBattery ? 1 : 0); out.writeInt(mEstimatedBatteryCapacity); + out.writeInt(mMinLearnedBatteryCapacity); + out.writeInt(mMaxLearnedBatteryCapacity); mOnBatteryTimeBase.writeToParcel(out, uSecUptime, uSecRealtime); mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime); diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index cc3f58cb0c3d..e28079fd5cdd 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -70,6 +70,7 @@ class WebViewZygoteInit { @Override protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { + Log.i(TAG, "Beginning package preload"); // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that // our children will reuse the same classloader instead of creating their own. // This enables us to preload Java and native code in the webview zygote process and @@ -97,6 +98,7 @@ class WebViewZygoteInit { IllegalAccessException | InvocationTargetException e) { Log.e(TAG, "Exception while preloading package", e); } + Log.i(TAG, "Package preload done"); return false; } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index baf6db93daaf..80b6b0848a0b 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -82,6 +82,7 @@ import android.widget.PopupWindow; import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -2182,19 +2183,22 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final boolean wasAdjustedForStack = mElevationAdjustedForStack; // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null) // since the shadow is bound to the content size and not the target size. - if (StackId.hasWindowShadow(mStackId) && !isResizing()) { + if ((mStackId == FREEFORM_WORKSPACE_STACK_ID) && !isResizing()) { elevation = hasWindowFocus() ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; // Add a maximum shadow height value to the top level view. // Note that pinned stack doesn't have focus // so maximum shadow height adjustment isn't needed. // TODO(skuhne): Remove this if clause once b/22668382 got fixed. - if (!mAllowUpdateElevation && mStackId != PINNED_STACK_ID) { + if (!mAllowUpdateElevation) { elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; } // Convert the DP elevation into physical pixels. elevation = dipToPx(elevation); mElevationAdjustedForStack = true; + } else if (mStackId == PINNED_STACK_ID) { + elevation = dipToPx(DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP); + mElevationAdjustedForStack = true; } else { mElevationAdjustedForStack = false; } diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java index 44b21b44e93b..5cb66e501393 100644 --- a/core/java/com/android/internal/util/NotificationColorUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -460,13 +460,25 @@ public class NotificationColorUtil { if (backgroundColor == Notification.COLOR_DEFAULT) { return context.getColor(com.android.internal.R.color.notification_action_list); } - boolean useDark = shouldUseDark(backgroundColor); + return getShiftedColor(backgroundColor, 7); + } + + /** + * Get a color that stays in the same tint, but darkens or lightens it by a certain + * amount. + * This also looks at the lightness of the provided color and shifts it appropriately. + * + * @param color the base color to use + * @param amount the amount from 1 to 100 how much to modify the color + * @return the now color that was modified + */ + public static int getShiftedColor(int color, int amount) { final double[] result = ColorUtilsFromCompat.getTempDouble3Array(); - ColorUtilsFromCompat.colorToLAB(backgroundColor, result); - if (useDark && result[0] < 97 || !useDark && result[0] < 4) { - result[0] = Math.min(100, result[0] + 7); + ColorUtilsFromCompat.colorToLAB(color, result); + if (result[0] >= 4) { + result[0] = Math.max(0, result[0] - amount); } else { - result[0] = Math.max(0, result[0] - 7); + result[0] = Math.min(100, result[0] + amount); } return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]); } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 818cc2c2421d..8c71cf7006ff 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -233,6 +233,7 @@ public final class FloatingToolbar { private void doShow() { List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu); + tidy(menuItems); if (!isCurrentlyShowing(menuItems) || mWidthChanged) { mPopup.dismiss(); mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth); @@ -274,6 +275,36 @@ public final class FloatingToolbar { return menuItems; } + /** + * Update the list of menu items to conform to certain requirements. + */ + private void tidy(List<MenuItem> menuItems) { + int assistItemIndex = -1; + Drawable assistItemDrawable = null; + + final int size = menuItems.size(); + for (int i = 0; i < size; i++) { + final MenuItem menuItem = menuItems.get(i); + + if (menuItem.getItemId() == android.R.id.textAssist) { + assistItemIndex = i; + assistItemDrawable = menuItem.getIcon(); + } + + // Remove icons for all menu items with text. + if (!TextUtils.isEmpty(menuItem.getTitle())) { + menuItem.setIcon(null); + } + } + if (assistItemIndex > -1) { + final MenuItem assistMenuItem = menuItems.remove(assistItemIndex); + // Ensure the assist menu item preserves its icon. + assistMenuItem.setIcon(assistItemDrawable); + // Ensure the assist menu item is always the first item. + menuItems.add(0, assistMenuItem); + } + } + private List<Object> getShowingMenuItemsReferences(List<MenuItem> menuItems) { List<Object> references = new ArrayList<Object>(); for (MenuItem menuItem : menuItems) { diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index c64ace403f76..b7028980b314 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Button; import android.widget.ImageView; @@ -30,6 +31,8 @@ import android.widget.RemoteViews; */ @RemoteViews.RemoteView public class NotificationExpandButton extends ImageView { + private View mLabeledBy; + public NotificationExpandButton(Context context) { super(context); } @@ -66,5 +69,12 @@ public class NotificationExpandButton extends ImageView { public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(Button.class.getName()); + if (mLabeledBy != null) { + info.setLabeledBy(mLabeledBy); + } + } + + public void setLabeledBy(View labeledBy) { + mLabeledBy = labeledBy; } } diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 4a9a2c55e940..c1f443f62cfc 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -35,6 +35,7 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; @@ -89,6 +90,11 @@ public class BootReceiver extends BroadcastReceiver { // example: fs_stat,/dev/block/platform/soc/by-name/userdata,0x5 private static final String FS_STAT_PATTERN = "fs_stat,[^,]*/([^/,]+),(0x[0-9a-fA-F]+)"; + private static final int FS_STAT_FS_FIXED = 0x400; // should match with fs_mgr.cpp:FsStatFlags + private static final String FSCK_PASS_PATTERN = "Pass ([1-9]E?):"; + private static final String FSCK_TREE_OPTIMIZATION_PATTERN = + "Inode [0-9]+ extent tree.*could be shorter"; + private static final String FSCK_FS_MODIFIED = "FILE SYSTEM WAS MODIFIED"; // ro.boottime.init.mount_all. + postfix for mount_all duration private static final String[] MOUNT_DURATION_PROPS_POSTFIX = new String[] { "early", "default", "late" }; @@ -334,17 +340,22 @@ public class BootReceiver extends BroadcastReceiver { String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); Pattern pattern = Pattern.compile(FS_STAT_PATTERN); - for (String line : log.split("\n")) { // should check all lines - if (line.contains("FILE SYSTEM WAS MODIFIED")) { + String lines[] = log.split("\n"); + int lineNumber = 0; + int lastFsStatLineNumber = 0; + for (String line : lines) { // should check all lines + if (line.contains(FSCK_FS_MODIFIED)) { uploadNeeded = true; } else if (line.contains("fs_stat")){ Matcher matcher = pattern.matcher(line); if (matcher.find()) { - handleFsckFsStat(matcher); + handleFsckFsStat(matcher, lines, lastFsStatLineNumber, lineNumber); + lastFsStatLineNumber = lineNumber; } else { Slog.w(TAG, "cannot parse fs_stat:" + line); } } + lineNumber++; } if (uploadEnabled && uploadNeeded ) { @@ -403,7 +414,88 @@ public class BootReceiver extends BroadcastReceiver { } } - private static void handleFsckFsStat(Matcher match) { + /** + * Fix fs_stat from e2fsck. + * For now, only handle the case of quota warning caused by tree optimization. Clear fs fix + * flag (=0x400) caused by that. + * + * @param partition partition name + * @param statOrg original stat reported from e2fsck log + * @param lines e2fsck logs broken down into lines + * @param startLineNumber start line to parse + * @param endLineNumber end line. exclusive. + * @return updated fs_stat. For tree optimization, will clear bit 0x400. + */ + @VisibleForTesting + public static int fixFsckFsStat(String partition, int statOrg, String[] lines, + int startLineNumber, int endLineNumber) { + int stat = statOrg; + if ((stat & FS_STAT_FS_FIXED) != 0) { + // fs was fixed. should check if quota warning was caused by tree optimization. + // This is not a real fix but optimization, so should not be counted as a fs fix. + Pattern passPattern = Pattern.compile(FSCK_PASS_PATTERN); + Pattern treeOptPattern = Pattern.compile(FSCK_TREE_OPTIMIZATION_PATTERN); + String currentPass = ""; + boolean foundTreeOptimization = false; + boolean foundQuotaFix = false; + boolean foundOtherFix = false; + String otherFixLine = null; + for (int i = startLineNumber; i < endLineNumber; i++) { + String line = lines[i]; + if (line.contains(FSCK_FS_MODIFIED)) { // no need to parse above this + break; + } else if (line.startsWith("Pass ")) { + Matcher matcher = passPattern.matcher(line); + if (matcher.find()) { + currentPass = matcher.group(1); + } + } else if (line.startsWith("Inode ")) { + Matcher matcher = treeOptPattern.matcher(line); + if (matcher.find() && currentPass.equals("1")) { + foundTreeOptimization = true; + Slog.i(TAG, "fs_stat, partition:" + partition + " found tree optimization:" + + line); + } else { + foundOtherFix = true; + otherFixLine = line; + break; + } + } else if (line.startsWith("[QUOTA WARNING]") && currentPass.equals("5")) { + Slog.i(TAG, "fs_stat, partition:" + partition + " found quota warning:" + + line); + foundQuotaFix = true; + if (!foundTreeOptimization) { // only quota warning, this is real fix. + otherFixLine = line; + break; + } + } else if (line.startsWith("Update quota info") && currentPass.equals("5")) { + // follows "[QUOTA WARNING]", ignore + } else { + line = line.trim(); + // ignore empty msg or any msg before Pass 1 + if (!line.isEmpty() && !currentPass.isEmpty()) { + foundOtherFix = true; + otherFixLine = line; + break; + } + } + } + if (!foundOtherFix && foundTreeOptimization && foundQuotaFix) { + // not a real fix, so clear it. + Slog.i(TAG, "fs_stat, partition:" + partition + + " quota fix due to tree optimization"); + stat &= ~FS_STAT_FS_FIXED; + } else { + if (otherFixLine != null) { + Slog.i(TAG, "fs_stat, partition:" + partition + " fix:" + otherFixLine); + } + } + } + return stat; + } + + private static void handleFsckFsStat(Matcher match, String[] lines, int startLineNumber, + int endLineNumber) { String partition = match.group(1); int stat; try { @@ -412,9 +504,9 @@ public class BootReceiver extends BroadcastReceiver { Slog.w(TAG, "cannot parse fs_stat: partition:" + partition + " stat:" + match.group(2)); return; } - + stat = fixFsckFsStat(partition, stat, lines, startLineNumber, endLineNumber); MetricsLogger.histogram(null, "boot_fs_stat_" + partition, stat); - Slog.i(TAG, "fs_stat, partition:" + partition + " stat:" + match.group(2)); + Slog.i(TAG, "fs_stat, partition:" + partition + " stat:0x" + Integer.toHexString(stat)); } private static HashMap<String, Long> readTimestamps() { diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 520302ee02b9..d0469961b674 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -24,6 +24,7 @@ #include <ScopedLocalRef.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_hardware_HardwareBuffer.h> +#include <vndk/hardware_buffer.h> #include <sensor/Sensor.h> #include <sensor/SensorEventQueue.h> #include <sensor/SensorManager.h> @@ -74,6 +75,12 @@ struct ListOffsets { jmethodID add; } gListOffsets; +struct StringOffsets { + jclass clazz; + jmethodID intern; + jstring emptyString; +} gStringOffsets; + /* * nativeClassInit is not inteneded to be thread-safe. It should be called before other native... * functions (except nativeCreate). @@ -83,75 +90,55 @@ nativeClassInit (JNIEnv *_env, jclass _this) { //android.hardware.Sensor SensorOffsets& sensorOffsets = gSensorOffsets; - jclass sensorClass = (jclass) _env->NewGlobalRef(_env->FindClass("android/hardware/Sensor")); - sensorOffsets.clazz = sensorClass; - sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;"); - sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;"); - sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I"); - sensorOffsets.handle = _env->GetFieldID(sensorClass, "mHandle", "I"); - sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F"); - sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F"); - sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F"); - sensorOffsets.minDelay = _env->GetFieldID(sensorClass, "mMinDelay", "I"); + jclass sensorClass = (jclass) + MakeGlobalRefOrDie(_env, FindClassOrDie(_env, "android/hardware/Sensor")); + sensorOffsets.clazz = sensorClass; + sensorOffsets.name = GetFieldIDOrDie(_env, sensorClass, "mName", "Ljava/lang/String;"); + sensorOffsets.vendor = GetFieldIDOrDie(_env, sensorClass, "mVendor", "Ljava/lang/String;"); + sensorOffsets.version = GetFieldIDOrDie(_env, sensorClass, "mVersion", "I"); + sensorOffsets.handle = GetFieldIDOrDie(_env, sensorClass, "mHandle", "I"); + sensorOffsets.range = GetFieldIDOrDie(_env, sensorClass, "mMaxRange", "F"); + sensorOffsets.resolution = GetFieldIDOrDie(_env, sensorClass, "mResolution","F"); + sensorOffsets.power = GetFieldIDOrDie(_env, sensorClass, "mPower", "F"); + sensorOffsets.minDelay = GetFieldIDOrDie(_env, sensorClass, "mMinDelay", "I"); sensorOffsets.fifoReservedEventCount = - _env->GetFieldID(sensorClass, "mFifoReservedEventCount", "I"); - sensorOffsets.fifoMaxEventCount = _env->GetFieldID(sensorClass, "mFifoMaxEventCount", "I"); - sensorOffsets.stringType = _env->GetFieldID(sensorClass, "mStringType", "Ljava/lang/String;"); - sensorOffsets.requiredPermission = _env->GetFieldID(sensorClass, "mRequiredPermission", - "Ljava/lang/String;"); - sensorOffsets.maxDelay = _env->GetFieldID(sensorClass, "mMaxDelay", "I"); - sensorOffsets.flags = _env->GetFieldID(sensorClass, "mFlags", "I"); - - sensorOffsets.setType = _env->GetMethodID(sensorClass, "setType", "(I)Z"); - sensorOffsets.setUuid = _env->GetMethodID(sensorClass, "setUuid", "(JJ)V"); - sensorOffsets.init = _env->GetMethodID(sensorClass, "<init>", "()V"); + GetFieldIDOrDie(_env,sensorClass, "mFifoReservedEventCount", "I"); + sensorOffsets.fifoMaxEventCount = GetFieldIDOrDie(_env,sensorClass, "mFifoMaxEventCount", "I"); + sensorOffsets.stringType = + GetFieldIDOrDie(_env,sensorClass, "mStringType", "Ljava/lang/String;"); + sensorOffsets.requiredPermission = + GetFieldIDOrDie(_env,sensorClass, "mRequiredPermission", "Ljava/lang/String;"); + sensorOffsets.maxDelay = GetFieldIDOrDie(_env,sensorClass, "mMaxDelay", "I"); + sensorOffsets.flags = GetFieldIDOrDie(_env,sensorClass, "mFlags", "I"); + + sensorOffsets.setType = GetMethodIDOrDie(_env,sensorClass, "setType", "(I)Z"); + sensorOffsets.setUuid = GetMethodIDOrDie(_env,sensorClass, "setUuid", "(JJ)V"); + sensorOffsets.init = GetMethodIDOrDie(_env,sensorClass, "<init>", "()V"); // java.util.List; ListOffsets& listOffsets = gListOffsets; - jclass listClass = (jclass) _env->NewGlobalRef(_env->FindClass("java/util/List")); + jclass listClass = (jclass) MakeGlobalRefOrDie(_env, FindClassOrDie(_env, "java/util/List")); listOffsets.clazz = listClass; - listOffsets.add = _env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z"); + listOffsets.add = GetMethodIDOrDie(_env,listClass, "add", "(Ljava/lang/Object;)Z"); + + // initialize java.lang.String and empty string intern + StringOffsets& stringOffsets = gStringOffsets; + stringOffsets.clazz = MakeGlobalRefOrDie(_env, FindClassOrDie(_env, "java/lang/String")); + stringOffsets.intern = + GetMethodIDOrDie(_env, stringOffsets.clazz, "intern", "()Ljava/lang/String;"); + ScopedLocalRef<jstring> empty(_env, _env->NewStringUTF("")); + stringOffsets.emptyString = (jstring) + MakeGlobalRefOrDie(_env, _env->CallObjectMethod(empty.get(), stringOffsets.intern)); } -/** - * A key comparator predicate. - * It is used to intern strings associated with Sensor data. - * It defines a 'Strict weak ordering' for the interned strings. - */ -class InternedStringCompare { -public: - bool operator()(const String8* string1, const String8* string2) const { - if (string1 == NULL) { - return string2 != NULL; - } - if (string2 == NULL) { - return false; - } - return string1->compare(*string2) < 0; +static jstring getJavaInternedString(JNIEnv *env, const String8 &string) { + if (string == "") { + return gStringOffsets.emptyString; } -}; -/** - * A localized interning mechanism for Sensor strings. - * We implement our own interning to avoid the overhead of using java.lang.String#intern(). - * It is common that Vendor, StringType, and RequirePermission data is common between many of the - * Sensors, by interning the memory usage to represent Sensors is optimized. - */ -static jstring -getInternedString(JNIEnv *env, const String8* string) { - static std::map<const String8*, jstring, InternedStringCompare> internedStrings; - - jstring internedString; - std::map<const String8*, jstring>::iterator iterator = internedStrings.find(string); - if (iterator != internedStrings.end()) { - internedString = iterator->second; - } else { - jstring localString = env->NewStringUTF(string->string()); - // we are implementing our own interning so expect these strings to be backed by global refs - internedString = (jstring) env->NewGlobalRef(localString); - internedStrings.insert(std::make_pair(string, internedString)); - env->DeleteLocalRef(localString); - } + ScopedLocalRef<jstring> javaString(env, env->NewStringUTF(string.string())); + jstring internedString = (jstring) + env->CallObjectMethod(javaString.get(), gStringOffsets.intern); return internedString; } @@ -173,10 +160,10 @@ translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nat } if (sensor != NULL) { - jstring name = env->NewStringUTF(nativeSensor.getName().string()); - jstring vendor = env->NewStringUTF(nativeSensor.getVendor().string()); + jstring name = getJavaInternedString(env, nativeSensor.getName()); + jstring vendor = getJavaInternedString(env, nativeSensor.getVendor()); jstring requiredPermission = - env->NewStringUTF(nativeSensor.getRequiredPermission().string()); + getJavaInternedString(env, nativeSensor.getRequiredPermission()); env->SetObjectField(sensor, sensorOffsets.name, name); env->SetObjectField(sensor, sensorOffsets.vendor, vendor); @@ -197,7 +184,7 @@ translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nat if (env->CallBooleanMethod(sensor, sensorOffsets.setType, nativeSensor.getType()) == JNI_FALSE) { - jstring stringType = getInternedString(env, &nativeSensor.getStringType()); + jstring stringType = getJavaInternedString(env, nativeSensor.getStringType()); env->SetObjectField(sensor, sensorOffsets.stringType, stringType); } diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index 678041f85d17..b21ea828f2a4 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -574,7 +574,7 @@ static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { size_t parentHandle; const hidl_string *s; - status_t err = parcel->readBuffer(&parentHandle, + status_t err = parcel->readBuffer(sizeof(*s), &parentHandle, reinterpret_cast<const void**>(&s)); if (err != OK) { @@ -583,7 +583,7 @@ static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { } err = ::android::hardware::readEmbeddedFromParcel( - const_cast<hidl_string *>(s), + const_cast<hidl_string &>(*s), *parcel, parentHandle, 0 /* parentOffset */); if (err != OK) { @@ -602,7 +602,7 @@ static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ size_t parentHandle; \ \ const hidl_vec<Type> *vec; \ - status_t err = parcel->readBuffer(&parentHandle, \ + status_t err = parcel->readBuffer(sizeof(*vec), &parentHandle, \ reinterpret_cast<const void**>(&vec)); \ \ if (err != OK) { \ @@ -613,7 +613,7 @@ static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ size_t childHandle; \ \ err = ::android::hardware::readEmbeddedFromParcel( \ - const_cast<hidl_vec<Type> *>(vec), \ + const_cast<hidl_vec<Type> &>(*vec), \ *parcel, \ parentHandle, \ 0 /* parentOffset */, \ @@ -645,7 +645,7 @@ static jbooleanArray JHwParcel_native_readBoolVector( size_t parentHandle; const hidl_vec<bool> *vec; - status_t err = parcel->readBuffer(&parentHandle, + status_t err = parcel->readBuffer(sizeof(*vec), &parentHandle, reinterpret_cast<const void**>(&vec)); if (err != OK) { @@ -656,7 +656,7 @@ static jbooleanArray JHwParcel_native_readBoolVector( size_t childHandle; err = ::android::hardware::readEmbeddedFromParcel( - const_cast<hidl_vec<bool> *>(vec), + const_cast<hidl_vec<bool> &>(*vec), *parcel, parentHandle, 0 /* parentOffset */, @@ -709,7 +709,7 @@ static jobjectArray JHwParcel_native_readStringVector( size_t parentHandle; const string_vec *vec; - status_t err = parcel->readBuffer(&parentHandle, + status_t err = parcel->readBuffer(sizeof(*vec), &parentHandle, reinterpret_cast<const void **>(&vec)); if (err != OK) { @@ -719,16 +719,15 @@ static jobjectArray JHwParcel_native_readStringVector( size_t childHandle; err = ::android::hardware::readEmbeddedFromParcel( - const_cast<string_vec *>(vec), + const_cast<string_vec &>(*vec), *parcel, parentHandle, 0 /* parentOffset */, &childHandle); for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { err = android::hardware::readEmbeddedFromParcel( - const_cast<hidl_vec<hidl_string> *>(vec), + const_cast<hidl_string &>((*vec)[i]), *parcel, childHandle, - i * sizeof(hidl_string), - nullptr /* childHandle */); + i * sizeof(hidl_string) /* parentOffset */); } if (err != OK) { @@ -810,13 +809,20 @@ static jobject JHwParcel_native_readStrongBinder(JNIEnv *env, jobject thiz) { return JHwRemoteBinder::NewObject(env, binder); } -static jobject JHwParcel_native_readBuffer(JNIEnv *env, jobject thiz) { +static jobject JHwParcel_native_readBuffer(JNIEnv *env, jobject thiz, + jlong expectedSize) { hardware::Parcel *parcel = JHwParcel::GetNativeContext(env, thiz)->getParcel(); size_t handle; const void *ptr; - status_t status = parcel->readBuffer(&handle, &ptr); + + if (expectedSize < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return nullptr; + } + + status_t status = parcel->readBuffer(expectedSize, &handle, &ptr); if (status != OK) { jniThrowException(env, "java/util/NoSuchElementException", NULL); @@ -827,8 +833,8 @@ static jobject JHwParcel_native_readBuffer(JNIEnv *env, jobject thiz) { } static jobject JHwParcel_native_readEmbeddedBuffer( - JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset, - jboolean nullable) { + JNIEnv *env, jobject thiz, jlong expectedSize, + jlong parentHandle, jlong offset, jboolean nullable) { hardware::Parcel *parcel = JHwParcel::GetNativeContext(env, thiz)->getParcel(); @@ -836,8 +842,13 @@ static jobject JHwParcel_native_readEmbeddedBuffer( const void *ptr; status_t status = - parcel->readNullableEmbeddedBuffer(&childHandle, parentHandle, offset, - &ptr); + parcel->readNullableEmbeddedBuffer(expectedSize, + &childHandle, parentHandle, offset, &ptr); + + if (expectedSize < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return nullptr; + } if (status != OK) { jniThrowException(env, "java/util/NoSuchElementException", NULL); @@ -952,10 +963,10 @@ static JNINativeMethod gMethods[] = { { "send", "()V", (void *)JHwParcel_native_send }, - { "readBuffer", "()L" PACKAGE_PATH "/HwBlob;", + { "readBuffer", "(J)L" PACKAGE_PATH "/HwBlob;", (void *)JHwParcel_native_readBuffer }, - { "readEmbeddedBuffer", "(JJZ)L" PACKAGE_PATH "/HwBlob;", + { "readEmbeddedBuffer", "(JJJZ)L" PACKAGE_PATH "/HwBlob;", (void *)JHwParcel_native_readEmbeddedBuffer }, { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V", diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 1aed501335c4..de67c50e7195 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -193,10 +193,10 @@ static void report_exception(JNIEnv* env, jthrowable excep, const char* msg) if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) { /* * It's an Error: Reraise the exception and ask the runtime to abort. - * This will dump the pending exception as well as all thread traces - * to the log. */ env->Throw(excep); + ALOGE("java.lang.Error thrown during binder transaction (stack trace follows) : "); + env->ExceptionDescribe(); env->FatalError("java.lang.Error thrown during binder transaction."); } diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index a2f07d989ab3..2042bf62ad4c 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -21,10 +21,14 @@ option java_outer_classname = "IncidentProtoMetadata"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/service/appwidget.proto"; +import "frameworks/base/core/proto/android/service/battery.proto"; import "frameworks/base/core/proto/android/service/graphicsstats.proto"; import "frameworks/base/core/proto/android/service/fingerprint.proto"; +import "frameworks/base/core/proto/android/service/diskstats.proto"; import "frameworks/base/core/proto/android/service/netstats.proto"; import "frameworks/base/core/proto/android/service/notification.proto"; +import "frameworks/base/core/proto/android/service/package.proto"; +import "frameworks/base/core/proto/android/service/power.proto"; import "frameworks/base/core/proto/android/providers/settings.proto"; package android.os; @@ -57,6 +61,10 @@ message IncidentProto { android.service.NetworkStatsServiceDumpProto netstats = 3001; android.providers.settings.SettingsServiceDumpProto settings = 3002; android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003; + android.service.battery.BatteryServiceDumpProto battery = 3006; + android.service.diskstats.DiskStatsServiceDumpProto diskstats = 3007; android.service.notification.NotificationServiceDumpProto notification = 3004; + android.service.pm.PackageServiceDumpProto package = 3008; + android.service.power.PowerServiceDumpProto power = 3009; android.service.GraphicsStatsServiceDumpProto graphicsstats = 3005; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8869593c2194..128c5ccb364c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -37,7 +37,6 @@ <protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" /> <protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" /> - <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_ADDED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" /> <protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" /> diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml deleted file mode 100644 index 89174312aaaf..000000000000 --- a/core/res/res/drawable/ic_corp_badge.xml +++ /dev/null @@ -1,33 +0,0 @@ -<!-- -Copyright (C) 2014 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="20.0dp" - android:height="20.0dp" - android:viewportWidth="20.0" - android:viewportHeight="20.0"> - <path - android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0" - android:fillColor="#FF5722"/> - <path - android:pathData="M15.2,6.2L4.8,6.2c-0.5,0.0 -0.9,0.4 -0.9,1.0L3.9,10.0c0.0,0.5 0.4,1.0 0.9,1.0l3.8,0.0l0.0,-1.0l2.9,0.0l0.0,1.0l3.8,0.0c0.5,0.0 1.0,-0.4 1.0,-1.0L16.3,7.1C16.2,6.6 15.8,6.2 15.2,6.2z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M8.6,12.9l0.0,-1.0L4.3,11.9l0.0,2.4c0.0,0.5 0.4,0.9 0.9,0.9l9.5,0.0c0.5,0.0 0.9,-0.4 0.9,-0.9l0.0,-2.4l-4.3,0.0l0.0,1.0L8.6,12.9z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M7.1,5.2l0.0,1.0 1.0,0.0 0.0,-1.0 3.799999,0.0 0.0,1.0 1.0,0.0 0.0,-1.0 -1.0,-0.9 -3.799999,0.0z" - android:fillColor="#FFFFFF"/> -</vector> diff --git a/core/res/res/drawable/ic_instant_icon_badge_bolt.xml b/core/res/res/drawable/ic_instant_icon_badge_bolt.xml new file mode 100644 index 000000000000..08512b7af310 --- /dev/null +++ b/core/res/res/drawable/ic_instant_icon_badge_bolt.xml @@ -0,0 +1,29 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="64.0dp" + android:height="64.0dp" + android:viewportWidth="64.0" + android:viewportHeight="64.0"> + + <!-- + The path is similar to ic_corp_icon_badge_case, such that it positions on top of + ic_corp_icon_badge_shadow. + --> + <path + android:pathData="M43.9 50.9h4v8l6-12h-4v-8l-6 12z" + android:fillColor="#757575"/> +</vector> diff --git a/core/res/res/drawable/ic_picture_in_picture.xml b/core/res/res/drawable/ic_picture_in_picture.xml new file mode 100644 index 000000000000..e2dda336f8f7 --- /dev/null +++ b/core/res/res/drawable/ic_picture_in_picture.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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="#FFFFFF" + android:pathData="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1 .9 +2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z" /> + <path + android:pathData="M0 0h24v24H0V0z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml index 0ef785f102bb..480defbd5935 100644 --- a/core/res/res/layout/accessibility_button_chooser.xml +++ b/core/res/res/layout/accessibility_button_chooser.xml @@ -19,7 +19,7 @@ <com.android.internal.widget.ResolverDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:maxWidth="@dimen/resolver_max_width" android:maxCollapsedHeight="256dp" android:maxCollapsedHeightSmall="56dp" @@ -27,11 +27,14 @@ <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:layout_alwaysShow="true" android:orientation="vertical" android:background="?attr/colorBackground" android:paddingTop="8dp" - android:paddingBottom="8dp"> + android:paddingBottom="8dp" + android:paddingStart="?attr/dialogPreferredPadding" + android:paddingEnd="?attr/dialogPreferredPadding"> <TextView android:layout_width="match_parent" @@ -41,8 +44,6 @@ android:text="@string/accessibility_button_prompt_text" android:gravity="start|center_vertical" android:layout_alignParentStart="true" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" android:paddingTop="8dp" android:paddingBottom="8dp"/> @@ -55,20 +56,15 @@ android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:stretchMode="columnWidth" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" android:gravity="center"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/accessibility_button_prompt" - android:layout_alwaysShow="true" android:textAppearance="?attr/textAppearanceMedium" android:text="@string/accessibility_button_instructional_text" android:gravity="start|center_vertical" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" android:paddingTop="8dp" android:paddingBottom="8dp" android:visibility="gone"/> diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml index 78eeee99150a..d62b93efc1a1 100644 --- a/core/res/res/values-television/config.xml +++ b/core/res/res/values-television/config.xml @@ -36,4 +36,7 @@ <!-- The default gravity for the picture-in-picture window. Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> <integer name="config_defaultPictureInPictureGravity">0x55</integer> + + <!-- Whether the device uses the default focus highlight when focus state isn't specified. --> + <bool name="config_useDefaultFocusHighlight">false</bool> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 569646820402..95ba94209343 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2439,6 +2439,14 @@ <!-- Whether the given RRO is static or not. --> <attr name="isStatic" format="boolean" /> + <!-- Required property name/value pair used to enable this overlay. + e.g. name=ro.oem.sku value=MKT210. + Overlay will be ignored unless system property exists and is + set to specified value --> + <!-- @hide @SystemApi This shouldn't be public. --> + <attr name="requiredSystemPropertyName" format="string" /> + <!-- @hide @SystemApi This shouldn't be public. --> + <attr name="requiredSystemPropertyValue" format="string" /> </declare-styleable> <!-- Declaration of an {@link android.content.Intent} object in XML. May diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 937fc6fba247..536906cde30c 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -171,6 +171,9 @@ <color name="profile_badge_2">#ff000000</color><!-- Black --> <color name="profile_badge_3">#ff22f033</color><!-- Green --> + <!-- Default instant app badge color --> + <color name="instant_app_badge">#FFFFFFFF</color><!-- White --> + <!-- Multi-sim sim colors --> <color name="Teal_700">#ff00796b</color> <color name="Teal_800">#ff00695c</color> diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index e0cc5b51e25b..8c37d4b053a1 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -74,10 +74,10 @@ <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item> <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item> - <item name="primary_content_alpha_material_light" format="float" type="dimen">1</item> - <item name="primary_content_alpha_material_dark" format="float" type="dimen">0.87</item> - <item name="secondary_content_alpha_material_light" format="float" type="dimen">.7</item> - <item name="secondary_content_alpha_material_dark" format="float" type="dimen">0.54</item> + <item name="primary_content_alpha_material_dark" format="float" type="dimen">1</item> + <item name="primary_content_alpha_material_light" format="float" type="dimen">0.87</item> + <item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item> + <item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item> <item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item> <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 45dccb6bfe33..27a4d5c2f5af 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -852,6 +852,8 @@ 1 - Go to sleep (doze) 2 - Really go to sleep (don't doze) 3 - Really go to sleep and go home (don't doze) + 4 - Go to home + 5 - Dismiss IME if shown. Otherwise go to home --> <integer name="config_shortPressOnPowerBehavior">1</integer> @@ -2794,7 +2796,7 @@ <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string> <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. --> - <string name="config_icon_mask" translatable="false">"M50,0L92,0 A8,8,0,0 1 100,8 L100,92 A8,8,0,0 1 92,100 L8,100 A8,8,0,0 1 0,92 L 0,8 A8,8,0,0 1 8,0z"</string> + <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string> <!-- The component name, flattened to a string, for the default accessibility service to be enabled by the accessibility shortcut. This service must be trusted, as it can be activated @@ -2828,4 +2830,7 @@ specified name exists on the device, autofill will be disabled by default. --> <string name="config_defaultAutofillService" translatable="false"></string> + + <!-- Whether the device uses the default focus highlight when focus state isn't specified. --> + <bool name="config_useDefaultFocusHighlight">true</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 213d6cafa72b..89c912fd1f83 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2817,6 +2817,10 @@ <public name="defaultFocusHighlightEnabled" /> <public name="persistentFeature"/> <public name="windowSplashscreenContent" /> + <!-- @hide @SystemApi --> + <public name="requiredSystemPropertyName" /> + <!-- @hide @SystemApi --> + <public name="requiredSystemPropertyValue" /> </public-group> <public-group type="style" first-id="0x010302e0"> @@ -2830,6 +2834,7 @@ <public-group type="drawable" first-id="0x010800b4"> <public name="autofilled_highlight" /> + <public name="ic_picture_in_picture" /> </public-group> <public-group type="string" first-id="0x01040019"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f2da8457601c..1dd8227edafd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -427,6 +427,7 @@ <java-symbol type="integer" name="config_mdc_initial_max_retry" /> <java-symbol type="integer" name="config_keepPreloadsMinDays" /> <java-symbol type="bool" name="config_hasPermanentDpad" /> + <java-symbol type="bool" name="config_useDefaultFocusHighlight" /> <java-symbol type="color" name="tab_indicator_text_v4" /> @@ -1322,6 +1323,7 @@ <java-symbol type="drawable" name="ic_corp_user_badge" /> <java-symbol type="drawable" name="ic_corp_badge_no_background" /> <java-symbol type="drawable" name="ic_corp_statusbar_icon" /> + <java-symbol type="drawable" name="ic_instant_icon_badge_bolt" /> <java-symbol type="drawable" name="emulator_circular_window_overlay" /> <java-symbol type="drawable" name="sim_light_blue" /> @@ -1353,6 +1355,7 @@ <java-symbol type="color" name="profile_badge_1" /> <java-symbol type="color" name="profile_badge_2" /> <java-symbol type="color" name="profile_badge_3" /> + <java-symbol type="color" name="instant_app_badge" /> <java-symbol type="layout" name="action_bar_home" /> <java-symbol type="layout" name="action_bar_title_item" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index e0b4ec5027bb..d80ff1d1e313 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -794,6 +794,8 @@ easier. <!-- Theme used for the intent picker activity. --> <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light"> + <item name="windowEnterTransition">@empty</item> + <item name="windowExitTransition">@empty</item> <item name="windowIsTranslucent">true</item> <item name="windowNoTitle">true</item> <item name="windowBackground">@color/transparent</item> diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java index bab9f631d7d4..63c1f87178aa 100644 --- a/core/tests/coretests/src/android/metrics/LogMakerTest.java +++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java @@ -263,4 +263,32 @@ public class LogMakerTest extends TestCase { assertFalse(a.isSubsetOf(b)); assertFalse(b.isSubsetOf(a)); } + + public void testConstructFromNull() { + new LogMaker(null); + // no promises, just don't throw + } + + public void testConstructFromNullKey() { + Object[] items = new Object[2]; + items[0] = null; + items[1] = "foo"; + new LogMaker(items); + // no promises, just don't throw + } + + public void testConstructFromNullField() { + Object[] items = new Object[2]; + items[0] = 10; + items[1] = null; + new LogMaker(items); + // no promises, just don't throw + } + + public void testConstructFromTruncatedArray() { + Object[] items = new Object[1]; + items[0] = 10; + new LogMaker(items); + // no promises, just don't throw + } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java new file mode 100644 index 000000000000..39b999d2c199 --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +import android.os.LocaleList; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.Locale; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TextClassificationManagerTest { + + private static final LocaleList LOCALES = LocaleList.forLanguageTags("en"); + + private TextClassificationManager mTcm; + private TextClassifier mClassifier; + + @Before + public void setup() { + mTcm = InstrumentationRegistry.getTargetContext() + .getSystemService(TextClassificationManager.class); + mTcm.setTextClassifier(null); + mClassifier = mTcm.getTextClassifier(); + } + + @Test + public void testSmartSelection() { + if (isTextClassifierDisabled()) return; + + String text = "Contact me at droid@android.com"; + String selected = "droid"; + String suggested = "droid@android.com"; + int startIndex = text.indexOf(selected); + int endIndex = startIndex + selected.length(); + int smartStartIndex = text.indexOf(suggested); + int smartEndIndex = smartStartIndex + suggested.length(); + + assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), + isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); + } + + @Test + public void testSmartSelection_nullLocaleList() { + if (isTextClassifierDisabled()) return; + + String text = "Contact me at droid@android.com"; + String selected = "droid"; + String suggested = "droid@android.com"; + int startIndex = text.indexOf(selected); + int endIndex = startIndex + selected.length(); + int smartStartIndex = text.indexOf(suggested); + int smartEndIndex = smartStartIndex + suggested.length(); + LocaleList nullLocales = null; + + assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, nullLocales), + isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); + } + + @Test + public void testSmartSelection_url() { + if (isTextClassifierDisabled()) return; + + String text = "Visit http://www.android.com for more information"; + String selected = "http"; + String suggested = "http://www.android.com"; + int startIndex = text.indexOf(selected); + int endIndex = startIndex + selected.length(); + int smartStartIndex = text.indexOf(suggested); + int smartEndIndex = smartStartIndex + suggested.length(); + + assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), + isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL)); + } + + @Test + public void testTextClassificationResult() { + if (isTextClassifierDisabled()) return; + + String text = "Contact me at droid@android.com"; + String classifiedText = "droid@android.com"; + int startIndex = text.indexOf(classifiedText); + int endIndex = startIndex + classifiedText.length(); + assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES), + isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL)); + } + + @Test + public void testTextClassificationResult_url() { + if (isTextClassifierDisabled()) return; + + String text = "Visit http://www.android.com for more information"; + String classifiedText = "http://www.android.com"; + int startIndex = text.indexOf(classifiedText); + int endIndex = startIndex + classifiedText.length(); + assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES), + isTextClassificationResult(classifiedText, TextClassifier.TYPE_URL)); + } + + @Test + public void testTextClassificationResult_nullLocaleList() { + if (isTextClassifierDisabled()) return; + + String text = "Contact me at droid@android.com"; + String classifiedText = "droid@android.com"; + int startIndex = text.indexOf(classifiedText); + int endIndex = startIndex + classifiedText.length(); + LocaleList nullLocales = null; + assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, nullLocales), + isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL)); + } + + @Test + public void testLanguageDetection() { + if (isTextClassifierDisabled()) return; + + String text = "This is a piece of English text"; + assertThat(mTcm.detectLanguages(text), isDetectedLanguage("en")); + + text = "Das ist ein deutscher Text"; + assertThat(mTcm.detectLanguages(text), isDetectedLanguage("de")); + + text = "これは日本語のテキストです"; + assertThat(mTcm.detectLanguages(text), isDetectedLanguage("ja")); + } + + @Test + public void testSetTextClassifier() { + TextClassifier classifier = mock(TextClassifier.class); + mTcm.setTextClassifier(classifier); + assertEquals(classifier, mTcm.getTextClassifier()); + } + + private boolean isTextClassifierDisabled() { + return mClassifier == TextClassifier.NO_OP; + } + + private static Matcher<TextSelection> isTextSelection( + final int startIndex, final int endIndex, final String type) { + return new BaseMatcher<TextSelection>() { + @Override + public boolean matches(Object o) { + if (o instanceof TextSelection) { + TextSelection selection = (TextSelection) o; + return startIndex == selection.getSelectionStartIndex() + && endIndex == selection.getSelectionEndIndex() + && selection.getEntityCount() > 0 + && type.equals(selection.getEntity(0)); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendValue( + String.format("%d, %d, %s", startIndex, endIndex, type)); + } + }; + } + + private static Matcher<TextClassificationResult> isTextClassificationResult( + final String text, final String type) { + return new BaseMatcher<TextClassificationResult>() { + @Override + public boolean matches(Object o) { + if (o instanceof TextClassificationResult) { + TextClassificationResult result = (TextClassificationResult) o; + return text.equals(result.getText()) + && result.getEntityCount() > 0 + && type.equals(result.getEntity(0)); + // TODO: Include other properties. + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("text=").appendValue(text) + .appendText(", type=").appendValue(type); + } + }; + } + + private static Matcher<List<TextLanguage>> isDetectedLanguage(final String language) { + return new BaseMatcher<List<TextLanguage>>() { + @Override + public boolean matches(Object o) { + if (o instanceof List) { + List languages = (List) o; + if (!languages.isEmpty()) { + Object o1 = languages.get(0); + if (o1 instanceof TextLanguage) { + TextLanguage lang = (TextLanguage) o1; + return lang.getLanguageCount() > 0 + && new Locale(language).getLanguage() + .equals(lang.getLanguage(0).getLanguage()); + } + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendValue(String.format("%s", language)); + } + }; + } +} diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 3029134251dd..2203b6abb8fd 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -28,6 +28,7 @@ import static android.widget.espresso.TextViewActions.longPressAndDragOnText; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; +import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem; @@ -46,6 +47,11 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.is; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; +import android.view.textclassifier.TextClassificationManager; +import android.view.textclassifier.TextClassifier; import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider; import android.support.test.espresso.action.EspressoKey; @@ -71,7 +77,8 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV @Override public void setUp() throws Exception { super.setUp(); - getActivity(); + getActivity().getSystemService(TextClassificationManager.class) + .setTextClassifier(TextClassifier.NO_OP); } public void testTypedTextIsOnScreen() throws Exception { @@ -676,4 +683,38 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV // hasTransientState should return false when selection is created by API. assertFalse(textView.hasTransientState()); } + + public void testAssistItemIsAtIndexZero() throws Exception { + getActivity().getSystemService(TextClassificationManager.class).setTextClassifier(null); + final TextView textView = (TextView) getActivity().findViewById(R.id.textview); + textView.post(() -> textView.setCustomSelectionActionModeCallback( + new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + // Create another item at order position 0 to confirm that it will never be + // placed before the textAssist item. + menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test"); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { + return false; + } + + @Override + public void onDestroyActionMode(ActionMode actionMode) {} + })); + final String text = "droid@android.com"; + + onView(withId(R.id.textview)).perform(replaceText(text)); + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@'))); + sleepForFloatingToolbarPopup(); + assertFloatingToolbarItemIndex(android.R.id.textAssist, 0); + } } diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java index 838f4db76165..5206c9b553a6 100644 --- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java @@ -29,7 +29,13 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; +import android.view.MenuItem; +import android.view.ViewGroup; +import java.util.ArrayList; +import java.util.List; +import org.hamcrest.Description; import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; import android.support.test.espresso.NoMatchingRootException; import android.support.test.espresso.NoMatchingViewException; @@ -123,6 +129,39 @@ public class FloatingToolbarEspressoUtils { } /** + * Asserts that the floating toolbar contains a specified item at a specified index. + * + * @param menuItemId id of the menu item + * @param index expected index of the menu item in the floating toolbar + * @throws AssertionError if the assertion fails + */ + public static void assertFloatingToolbarItemIndex(final int menuItemId, final int index) { + onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() { + private List<Integer> menuItemIds = new ArrayList<>(); + + @Override + public boolean matchesSafely(View view) { + collectMenuItemIds(view); + return menuItemIds.size() > index && menuItemIds.get(index) == menuItemId; + } + + @Override + public void describeTo(Description description) {} + + private void collectMenuItemIds(View view) { + if (view.getTag() instanceof MenuItem) { + menuItemIds.add(((MenuItem) view.getTag()).getItemId()); + } else if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + collectMenuItemIds(viewGroup.getChildAt(i)); + } + } + } + })); + } + + /** * Asserts that the floating toolbar doesn't contain the specified item. * * @param itemLabel label of the item. diff --git a/core/tests/overlaytests/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/OverlayAppFiltered/Android.mk new file mode 100644 index 000000000000..8ba21df189f0 --- /dev/null +++ b/core/tests/overlaytests/OverlayAppFiltered/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_JAVA_LIBRARIES += legacy-test + +LOCAL_SDK_VERSION := system_current + +LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay + +include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/OverlayAppFiltered/AndroidManifest.xml b/core/tests/overlaytests/OverlayAppFiltered/AndroidManifest.xml new file mode 100644 index 000000000000..5b7950a25fbf --- /dev/null +++ b/core/tests/overlaytests/OverlayAppFiltered/AndroidManifest.xml @@ -0,0 +1,9 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.overlaytest.filtered_app_overlay" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.overlaytest" + android:requiredSystemPropertyName="persist.oem.overlay.test" + android:requiredSystemPropertyValue="foo" + android:priority="3"/> +</manifest> diff --git a/core/tests/overlaytests/OverlayAppFiltered/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/OverlayAppFiltered/res/raw/lorem_ipsum.txt new file mode 100644 index 000000000000..0954cedeb5d5 --- /dev/null +++ b/core/tests/overlaytests/OverlayAppFiltered/res/raw/lorem_ipsum.txt @@ -0,0 +1 @@ +Lorem ipsum: filtered overlays. diff --git a/core/tests/overlaytests/OverlayAppFiltered/res/values/config.xml b/core/tests/overlaytests/OverlayAppFiltered/res/values/config.xml new file mode 100644 index 000000000000..60b94eec5994 --- /dev/null +++ b/core/tests/overlaytests/OverlayAppFiltered/res/values/config.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="str">filtered</string> +</resources> diff --git a/core/tests/overlaytests/OverlayAppFiltered/res/xml/integer.xml b/core/tests/overlaytests/OverlayAppFiltered/res/xml/integer.xml new file mode 100644 index 000000000000..e2652b7e2915 --- /dev/null +++ b/core/tests/overlaytests/OverlayAppFiltered/res/xml/integer.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<integer value="3"/> diff --git a/core/tests/overlaytests/OverlayAppFirst/Android.mk b/core/tests/overlaytests/OverlayAppFirst/Android.mk index ee991fcd919a..51f4487fb25f 100644 --- a/core/tests/overlaytests/OverlayAppFirst/Android.mk +++ b/core/tests/overlaytests/OverlayAppFirst/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_LIBRARIES += legacy-test LOCAL_SDK_VERSION := current diff --git a/core/tests/overlaytests/OverlayAppSecond/Android.mk b/core/tests/overlaytests/OverlayAppSecond/Android.mk index 87402c43dd9a..b3cfd1817cbf 100644 --- a/core/tests/overlaytests/OverlayAppSecond/Android.mk +++ b/core/tests/overlaytests/OverlayAppSecond/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_LIBRARIES += legacy-test LOCAL_SDK_VERSION := current diff --git a/core/tests/overlaytests/OverlayTest/Android.mk b/core/tests/overlaytests/OverlayTest/Android.mk index 4767e52c0816..964348fab881 100644 --- a/core/tests/overlaytests/OverlayTest/Android.mk +++ b/core/tests/overlaytests/OverlayTest/Android.mk @@ -7,6 +7,8 @@ LOCAL_PACKAGE_NAME := OverlayTest LOCAL_DEX_PREOPT := false +LOCAL_JAVA_LIBRARIES += legacy-test + LOCAL_MODULE_PATH := $(TARGET_OUT)/app LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk index b1327f713ae7..5265d9169f7f 100644 --- a/core/tests/overlaytests/OverlayTestOverlay/Android.mk +++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_LIBRARIES += legacy-test LOCAL_SDK_VERSION := current diff --git a/core/tests/overlaytests/testrunner.py b/core/tests/overlaytests/testrunner.py index 2aa25adeda5d..e88805e8cbf1 100755 --- a/core/tests/overlaytests/testrunner.py +++ b/core/tests/overlaytests/testrunner.py @@ -13,6 +13,7 @@ TASK_COMPILATION = 'compile' TASK_DISABLE_OVERLAYS = 'disable overlays' TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays' TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay' +TASK_ENABLE_FILTERED_OVERLAYS = 'enable filtered overlays' TASK_FILE_EXISTS_TEST = 'test (file exists)' TASK_GREP_IDMAP_TEST = 'test (grep idmap)' TASK_MD5_TEST = 'test (md5)' @@ -25,6 +26,7 @@ TASK_PUSH = 'push' TASK_ROOT = 'root' TASK_REMOUNT = 'remount' TASK_RM = 'rm' +TASK_SETPROP = 'setprop' TASK_SETUP_IDMAP_PATH = 'setup idmap --path' TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan' TASK_START = 'start' @@ -188,7 +190,10 @@ class PushTask: return "%s -> %s" % (self.src, self.dest) def execute(self): - src = os.getenv('OUT') + "/" + self.src + src = os.getenv('OUT') + if (src is None): + return 1, "", "Unable to proceed - $OUT environment var not set\n" + src += "/" + self.src argv = shlex.split(adb + ' push %s %s' % (src, self.dest)) proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = proc.communicate() @@ -219,10 +224,24 @@ class RmTask: def execute(self): returncode, stdout, stderr = _adb_shell('ls %s' % self.path) - if returncode != 0 and stdout.endswith(': No such file or directory\n'): + if returncode != 0 and stderr.endswith(': No such file or directory\n'): return 0, "", "" return _adb_shell('rm -r %s' % self.path) +class SetPropTask: + def __init__(self, prop, value): + self.prop = prop + self.value = value + + def get_type(self): + return TASK_SETPROP + + def get_name(self): + return self.prop + + def execute(self): + return _adb_shell('setprop %s %s' % (self.prop, self.value)) + class IdmapPathTask: def __init__(self, path_target_apk, path_overlay_apk, path_idmap): self.path_target_apk = path_target_apk @@ -236,7 +255,7 @@ class IdmapPathTask: return self.path_idmap def execute(self): - return _adb_shell('su system idmap --path "%s" "%s" "%s"' % (self.path_target_apk, self.path_overlay_apk, self.path_idmap)) + return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.target_pkg_name, self.target_pkg, self.idmap_dir, self.overlay_dir)) class IdmapScanTask: def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir): @@ -411,8 +430,12 @@ def _create_disable_overlays_task(): RmTask("/data/resource-cache/vendor@overlay@framework_b.apk@idmap"), RmTask("/vendor/overlay/app_a.apk"), RmTask("/vendor/overlay/app_b.apk"), + RmTask("/vendor/overlay/app_c.apk"), RmTask("/data/resource-cache/vendor@overlay@app_a.apk@idmap"), RmTask("/data/resource-cache/vendor@overlay@app_b.apk@idmap"), + RmTask("/data/resource-cache/vendor@overlay@app_c.apk@idmap"), + SetPropTask('persist.oem.overlay.test', '""'), + RmTask("/data/property/persist.oem.overlay.test"), ] return CompoundTask(TASK_DISABLE_OVERLAYS, tasks) @@ -435,9 +458,23 @@ def _create_enable_multiple_overlays_task(): PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'), PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'), PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'), + PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'), ] return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks) +def _create_enable_filtered_overlays_task(): + tasks = [ + _create_disable_overlays_task(), + SetPropTask('persist.oem.overlay.test', 'foo'), + MkdirTask('/system/vendor'), + MkdirTask('/vendor/overlay'), + PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'), + PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'), + PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'), + PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'), + ] + return CompoundTask(TASK_ENABLE_FILTERED_OVERLAYS, tasks) + def _create_setup_idmap_path_task(idmaps, symlinks): tasks = [ _create_enable_single_overlay_task(), @@ -450,12 +487,11 @@ def _create_setup_idmap_path_task(idmaps, symlinks): def _create_setup_idmap_scan_task(idmaps, symlinks): tasks = [ - _create_enable_single_overlay_task(), + _create_enable_filtered_overlays_task(), RmTask(symlinks), RmTask(idmaps), MkdirTask(idmaps), MkdirTask(symlinks), - _create_enable_multiple_overlays_task(), ] return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks) @@ -538,7 +574,7 @@ def _create_opt_parser(): help='do not rebuild test projects') parser.add_option('-i', '--test-idmap', action='store_true', dest='test_idmap', default=False, - help='run tests for single overlay') + help='run tests for idmap') parser.add_option('-0', '--test-no-overlay', action='store_true', dest='test_no_overlay', default=False, help='run tests without any overlay') @@ -548,16 +584,21 @@ def _create_opt_parser(): parser.add_option('-2', '--test-multiple-overlays', action='store_true', dest='test_multiple_overlays', default=False, help='run tests for multiple overlays') + parser.add_option('-3', '--test-filtered-overlays', action='store_true', + dest='test_filtered_overlays', default=False, + help='run tests for filtered (sys prop) overlays') return parser if __name__ == '__main__': opt_parser = _create_opt_parser() opts, args = opt_parser.parse_args(sys.argv[1:]) - if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays: + if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays and not opts.test_filtered_overlays: opts.test_idmap = True opts.test_no_overlay = True opts.test_single_overlay = True opts.test_multiple_overlays = True + opts.test_filtered_overlays = True + if len(args) > 0: opt_parser.error("unexpected arguments: %s" % " ".join(args)) # will never reach this: opt_parser.error will call sys.exit @@ -580,6 +621,7 @@ if __name__ == '__main__': tasks.append(CompilationTask('OverlayTestOverlay/Android.mk')) tasks.append(CompilationTask('OverlayAppFirst/Android.mk')) tasks.append(CompilationTask('OverlayAppSecond/Android.mk')) + tasks.append(CompilationTask('OverlayAppFiltered/Android.mk')) # remount filesystem, install test project tasks.append(RootTask()) @@ -600,13 +642,13 @@ if __name__ == '__main__': tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1)) # idmap --scan - idmap = idmaps + '/vendor@overlay@framework_b.apk@idmap' tasks.append(StopTask()) tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks)) tasks.append(StartTask()) tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks)) - tasks.append(FileExistsTest(idmap)) - tasks.append(GrepIdmapTest(idmap, 'bool/config_annoy_dianne', 1)) + tasks.append(FileExistsTest(idmaps + '/vendor@overlay@framework_b.apk@idmap')) + tasks.append(GrepIdmapTest(idmaps + '/vendor@overlay@framework_b.apk@idmap', 'bool/config_annoy_dianne', 1)) + # overlays.list overlays_list_path = idmaps + '/overlays.list' @@ -620,27 +662,38 @@ if __name__ == '__main__': tasks.append(RmTask(symlinks)) tasks.append(RmTask(idmaps)) - # test no overlay + # test no overlay: all overlays cleared if opts.test_no_overlay: tasks.append(StopTask()) tasks.append(_create_disable_overlays_task()) tasks.append(StartTask()) tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest')) - # test single overlay + # test single overlay: one overlay (a) if opts.test_single_overlay: tasks.append(StopTask()) tasks.append(_create_enable_single_overlay_task()) tasks.append(StartTask()) tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest')) - # test multiple overlays + # test multiple overlays: all overlays - including 'disabled' filtered + # overlay (system property unset) so expect 'b[p=2]' overrides 'a[p=1]' but + # 'c[p=3]' should be ignored if opts.test_multiple_overlays: tasks.append(StopTask()) tasks.append(_create_enable_multiple_overlays_task()) tasks.append(StartTask()) tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest')) + # test filtered overlays: all overlays - including 'enabled' filtered + # overlay (system property set/matched) so expect c[p=3] to override both a + # & b where applicable + if opts.test_filtered_overlays: + tasks.append(StopTask()) + tasks.append(_create_enable_filtered_overlays_task()) + tasks.append(StartTask()) + tasks.append(InstrumentationTask('com.android.overlaytest.WithFilteredOverlaysTest')) + ignored_errors = 0 for t in tasks: type = t.get_type() diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 7a8e4873a7c9..abdab39afd6b 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -916,17 +916,20 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If null or if the config is not - * {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, + * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the + * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} + * is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always * immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB}, - * or if the specified color space's transfer function is not an - * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve} + * if the specified color space's transfer function is not an + * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if + * the color space is null */ public static Bitmap createBitmap(int width, int height, @NonNull Config config, - boolean hasAlpha, @Nullable ColorSpace colorSpace) { + boolean hasAlpha, @NonNull ColorSpace colorSpace) { return createBitmap(null, width, height, config, hasAlpha, colorSpace); } @@ -950,7 +953,8 @@ public final class Bitmap implements Parcelable { */ public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height, @NonNull Config config, boolean hasAlpha) { - return createBitmap(display, width, height, config, hasAlpha, null); + return createBitmap(display, width, height, config, hasAlpha, + ColorSpace.get(ColorSpace.Named.SRGB)); } /** @@ -967,26 +971,34 @@ public final class Bitmap implements Parcelable { * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. - * @param colorSpace The color space of the bitmap. If null or if the config is not - * {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} is assumed. + * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}, + * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the + * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} + * is assumed. * * @throws IllegalArgumentException if the width or height are <= 0, if * Config is Config.HARDWARE (because hardware bitmaps are always * immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB}, - * or if the specified color space's transfer function is not an - * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve} + * if the specified color space's transfer function is not an + * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if + * the color space is null */ public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height, - @NonNull Config config, boolean hasAlpha, @Nullable ColorSpace colorSpace) { + @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("width and height must be > 0"); } if (config == Config.HARDWARE) { throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE"); } + if (colorSpace == null) { + throw new IllegalArgumentException("can't create bitmap without a color space"); + } Bitmap bm; - if (colorSpace == null || config != Config.ARGB_8888) { + // nullptr color spaces have a particular meaning in native and are interpreted as sRGB + // (we also avoid the unnecessary extra work of the else branch) + if (config != Config.ARGB_8888 || colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) { bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null); } else { if (!(colorSpace instanceof ColorSpace.Rgb)) { diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java index 7e6756ebfdba..5577f53ee28b 100644 --- a/graphics/java/android/graphics/BitmapShader.java +++ b/graphics/java/android/graphics/BitmapShader.java @@ -41,35 +41,16 @@ public class BitmapShader extends Shader { * @param tileY The tiling mode for y to draw the bitmap in. */ public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) { - set(bitmap, tileX, tileY); + this(bitmap, tileX.nativeInt, tileY.nativeInt); } private BitmapShader(Bitmap bitmap, int tileX, int tileY) { - setInternal(bitmap, tileX, tileY); - } - - /** - * Reinitialize the BitmapShader's Bitmap and tile modes. - * - * @param bitmap The bitmap to use inside the shader - * @param tileX The tiling mode for x to draw the bitmap in. - * @param tileY The tiling mode for y to draw the bitmap in. - */ - public void set(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) { - if (tileX == null || tileY == null) { - throw new IllegalArgumentException(); - } - setInternal(bitmap, tileX.nativeInt, tileY.nativeInt); - } - - private void setInternal(Bitmap bitmap, int tileX, int tileY) { if (bitmap == null) { throw new IllegalArgumentException("Bitmap must be non-null"); } if (bitmap == mBitmap && tileX == mTileX && tileY == mTileY) { return; } - discardNativeInstance(); mBitmap = bitmap; mTileX = tileX; mTileY = tileY; diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java index 61f6cc5ba4ee..9201a2e2310e 100644 --- a/graphics/java/android/graphics/ColorMatrixColorFilter.java +++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java @@ -73,6 +73,8 @@ public class ColorMatrixColorFilter extends ColorFilter { * @see #getColorMatrix(ColorMatrix) * @see #setColorMatrixArray(float[]) * @see ColorMatrix#reset() + * + * @hide */ public void setColorMatrix(@Nullable ColorMatrix matrix) { discardNativeInstance(); @@ -99,6 +101,8 @@ public class ColorMatrixColorFilter extends ColorFilter { * * @throws ArrayIndexOutOfBoundsException if the specified array's * length is < 20 + * + * @hide */ public void setColorMatrixArray(@Nullable float[] array) { // called '...Array' so that passing null isn't ambiguous diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java index 8438bf2cc6b4..0b1141a0e505 100644 --- a/graphics/java/android/graphics/ComposeShader.java +++ b/graphics/java/android/graphics/ComposeShader.java @@ -59,43 +59,10 @@ public class ComposeShader extends Shader { } private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) { - setInternal(shaderA, shaderB, nativeMode); - } - - /** - * Reinitialize the ComposeShader's component Shaders and blend mode. - * - * @param shaderA The colors from this shader are seen as the "dst" by the mode - * @param shaderB The colors from this shader are seen as the "src" by the mode - * @param mode The PorterDuff mode that combines the colors from the two shaders. - */ - public void set(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) { - setInternal(shaderA, shaderB, mode.porterDuffMode); - } - - /** - * Reinitialize the ComposeShader's component Shaders and blend mode. - * - * @param shaderA The colors from this shader are seen as the "dst" by the mode - * @param shaderB The colors from this shader are seen as the "src" by the mode - * @param mode The PorterDuff mode that combines the colors from the two shaders. - */ - public void set(@NonNull Shader shaderA, @NonNull Shader shaderB, - @NonNull PorterDuff.Mode mode) { - setInternal(shaderA, shaderB, mode.nativeInt); - } - - private void setInternal(Shader shaderA, Shader shaderB, int nativeMode) { if (shaderA == null || shaderB == null) { throw new IllegalArgumentException("Shader parameters must not be null"); } - if (shaderA == mShaderA && shaderB == mShaderB && mPorterDuffMode == nativeMode) { - // no work to do... - return; - } - - discardNativeInstance(); mShaderA = shaderA; mShaderB = shaderB; mPorterDuffMode = nativeMode; diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java index b0c145beb649..1578ffb873f0 100644 --- a/graphics/java/android/graphics/LightingColorFilter.java +++ b/graphics/java/android/graphics/LightingColorFilter.java @@ -57,8 +57,6 @@ public class LightingColorFilter extends ColorFilter { /** * Returns the RGB color used to multiply the source color when the * color filter is applied. - * - * @see #setColorMultiply(int) */ @ColorInt public int getColorMultiply() { @@ -71,6 +69,8 @@ public class LightingColorFilter extends ColorFilter { * The alpha channel of this color is ignored. * * @see #getColorMultiply() + * + * @hide */ public void setColorMultiply(@ColorInt int mul) { if (mMul != mul) { @@ -82,8 +82,6 @@ public class LightingColorFilter extends ColorFilter { /** * Returns the RGB color that will be added to the source color * when the color filter is applied. - * - * @see #setColorAdd(int) */ @ColorInt public int getColorAdd() { @@ -96,6 +94,8 @@ public class LightingColorFilter extends ColorFilter { * The alpha channel of this color is ignored. * * @see #getColorAdd() + * + * @hide */ public void setColorAdd(@ColorInt int add) { if (mAdd != add) { diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java index 0e4cd0ae06b6..7139efec9337 100644 --- a/graphics/java/android/graphics/LinearGradient.java +++ b/graphics/java/android/graphics/LinearGradient.java @@ -57,48 +57,12 @@ public class LinearGradient extends Shader { */ public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) { - set(x0, y0, x1, y1, colors, positions, tile); - } - - /** - * Create a shader that draws a linear gradient along a line. - * - * @param x0 The x-coordinate for the start of the gradient line - * @param y0 The y-coordinate for the start of the gradient line - * @param x1 The x-coordinate for the end of the gradient line - * @param y1 The y-coordinate for the end of the gradient line - * @param color0 The color at the start of the gradient line. - * @param color1 The color at the end of the gradient line. - * @param tile The Shader tiling mode - */ - public LinearGradient(float x0, float y0, float x1, float y1, - @ColorInt int color0, @ColorInt int color1, - @NonNull TileMode tile) { - set(x0, y0, x1, y1, color0, color1, tile); - } - - /** - * Reinitialize the shader. - * - * @param x0 The x-coordinate for the start of the gradient line - * @param y0 The y-coordinate for the start of the gradient line - * @param x1 The x-coordinate for the end of the gradient line - * @param y1 The y-coordinate for the end of the gradient line - * @param colors The colors to be distributed along the gradient line - * @param positions May be null. The relative positions [0..1] of - * each corresponding color in the colors array. If this is null, - * the the colors are distributed evenly along the gradient line. - * @param tile The Shader tiling mode - */ - public void set(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], - @Nullable float positions[], @NonNull TileMode tile) { if (colors.length < 2) { throw new IllegalArgumentException("needs >= 2 number of colors"); } if (positions != null && colors.length != positions.length) { throw new IllegalArgumentException("color and position arrays must be of equal length"); } - discardNativeInstance(); mType = TYPE_COLORS_AND_POSITIONS; mX0 = x0; mY0 = y0; @@ -110,7 +74,7 @@ public class LinearGradient extends Shader { } /** - * Reinitialize the shader. + * Create a shader that draws a linear gradient along a line. * * @param x0 The x-coordinate for the start of the gradient line * @param y0 The y-coordinate for the start of the gradient line @@ -120,10 +84,9 @@ public class LinearGradient extends Shader { * @param color1 The color at the end of the gradient line. * @param tile The Shader tiling mode */ - public void set(float x0, float y0, float x1, float y1, + public LinearGradient(float x0, float y0, float x1, float y1, @ColorInt int color0, @ColorInt int color1, @NonNull TileMode tile) { - discardNativeInstance(); mType = TYPE_COLOR_START_AND_COLOR_END; mX0 = x0; mY0 = y0; diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java index ccc6eadc3df5..01d5825dd1e0 100644 --- a/graphics/java/android/graphics/PorterDuffColorFilter.java +++ b/graphics/java/android/graphics/PorterDuffColorFilter.java @@ -49,6 +49,8 @@ public class PorterDuffColorFilter extends ColorFilter { * * @see Color * @see #setColor(int) + * + * @hide */ @ColorInt public int getColor() { @@ -64,6 +66,8 @@ public class PorterDuffColorFilter extends ColorFilter { * @see Color * @see #getColor() * @see #getMode() + * + * @hide */ public void setColor(@ColorInt int color) { if (mColor != color) { @@ -78,6 +82,8 @@ public class PorterDuffColorFilter extends ColorFilter { * * @see PorterDuff * @see #setMode(android.graphics.PorterDuff.Mode) + * + * @hide */ public PorterDuff.Mode getMode() { return mMode; @@ -90,6 +96,8 @@ public class PorterDuffColorFilter extends ColorFilter { * @see PorterDuff * @see #getMode() * @see #getColor() + * + * @hide */ public void setMode(@NonNull PorterDuff.Mode mode) { if (mode == null) { diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java index ae8f7daf0298..f4b11917a415 100644 --- a/graphics/java/android/graphics/RadialGradient.java +++ b/graphics/java/android/graphics/RadialGradient.java @@ -57,39 +57,6 @@ public class RadialGradient extends Shader { public RadialGradient(float centerX, float centerY, float radius, @NonNull @ColorInt int colors[], @Nullable float stops[], @NonNull TileMode tileMode) { - set(centerX, centerY, radius, colors, stops, tileMode); - } - - /** - * Create a shader that draws a radial gradient given the center and radius. - * - * @param centerX The x-coordinate of the center of the radius - * @param centerY The y-coordinate of the center of the radius - * @param radius Must be positive. The radius of the circle for this gradient - * @param centerColor The color at the center of the circle. - * @param edgeColor The color at the edge of the circle. - * @param tileMode The Shader tiling mode - */ - public RadialGradient(float centerX, float centerY, float radius, - @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) { - set(centerX, centerY, radius, centerColor, edgeColor, tileMode); - } - - /** - * Reinitialize the shader. - * - * @param centerX The x-coordinate of the center of the radius - * @param centerY The y-coordinate of the center of the radius - * @param radius Must be positive. The radius of the circle for this gradient. - * @param colors The colors to be distributed between the center and edge of the circle - * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and - * <code>1.0f</code>. The relative position of each corresponding color in - * the colors array. If <code>null</code>, colors are distributed evenly - * between the center and edge of the circle. - * @param tileMode The Shader tiling mode - */ - public void set(float centerX, float centerY, float radius, - @NonNull @ColorInt int colors[], @Nullable float stops[], @NonNull TileMode tileMode) { if (radius <= 0) { throw new IllegalArgumentException("radius must be > 0"); } @@ -99,7 +66,6 @@ public class RadialGradient extends Shader { if (stops != null && colors.length != stops.length) { throw new IllegalArgumentException("color and position arrays must be of equal length"); } - discardNativeInstance(); mType = TYPE_COLORS_AND_POSITIONS; mX = centerX; mY = centerY; @@ -110,7 +76,7 @@ public class RadialGradient extends Shader { } /** - * Reinitialize the shader. + * Create a shader that draws a radial gradient given the center and radius. * * @param centerX The x-coordinate of the center of the radius * @param centerY The y-coordinate of the center of the radius @@ -119,12 +85,11 @@ public class RadialGradient extends Shader { * @param edgeColor The color at the edge of the circle. * @param tileMode The Shader tiling mode */ - public void set(float centerX, float centerY, float radius, + public RadialGradient(float centerX, float centerY, float radius, @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) { if (radius <= 0) { throw new IllegalArgumentException("radius must be > 0"); } - discardNativeInstance(); mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE; mX = centerX; mY = centerY; diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java index 0a1aef605562..b6b80b4f57dc 100644 --- a/graphics/java/android/graphics/SweepGradient.java +++ b/graphics/java/android/graphics/SweepGradient.java @@ -54,37 +54,6 @@ public class SweepGradient extends Shader { */ public SweepGradient(float cx, float cy, @NonNull @ColorInt int colors[], @Nullable float positions[]) { - set(cx, cy, colors, positions); - } - - /** - * A Shader that draws a sweep gradient around a center point. - * - * @param cx The x-coordinate of the center - * @param cy The y-coordinate of the center - * @param color0 The color to use at the start of the sweep - * @param color1 The color to use at the end of the sweep - */ - public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) { - set(cx, cy, color0, color1); - } - - /** - * Reinitialize the shader. - * - * @param cx The x-coordinate of the center - * @param cy The y-coordinate of the center - * @param colors The colors to be distributed between around the center. - * There must be at least 2 colors in the array. - * @param positions May be NULL. The relative position of - * each corresponding color in the colors array, beginning - * with 0 and ending with 1.0. If the values are not - * monotonic, the drawing may produce unexpected results. - * If positions is NULL, then the colors are automatically - * spaced evenly. - */ - public void set(float cx, float cy, - @NonNull @ColorInt int colors[], @Nullable float positions[]) { if (colors.length < 2) { throw new IllegalArgumentException("needs >= 2 number of colors"); } @@ -92,7 +61,6 @@ public class SweepGradient extends Shader { throw new IllegalArgumentException( "color and position arrays must be of equal length"); } - discardNativeInstance(); mType = TYPE_COLORS_AND_POSITIONS; mCx = cx; mCy = cy; @@ -101,15 +69,14 @@ public class SweepGradient extends Shader { } /** - * Reinitialize the shader. + * A Shader that draws a sweep gradient around a center point. * * @param cx The x-coordinate of the center * @param cy The y-coordinate of the center * @param color0 The color to use at the start of the sweep * @param color1 The color to use at the end of the sweep */ - public void set(float cx, float cy, @ColorInt int color0, @ColorInt int color1) { - discardNativeInstance(); + public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) { mType = TYPE_COLOR_START_AND_COLOR_END; mCx = cx; mCy = cy; diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 97d3e5ee5609..18dc0dc71dae 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -563,6 +563,9 @@ public class Typeface { /** * Constructs a builder with a file descriptor. * + * Caller is responsible for closing the passed file descriptor after {@link #build} is + * called. + * * @param fd The file descriptor. The passed fd must be mmap-able. */ public Builder(@NonNull FileDescriptor fd) { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index d95acff8a38e..3e5e3bfc3bf2 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -390,14 +390,8 @@ bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkCl } bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) { - SkRRect roundRect; - if (path->isRRect(&roundRect)) { - this->recordClip(roundRect, op); - mCanvas->clipRRect(roundRect, op); - } else { - this->recordClip(*path, op); - mCanvas->clipPath(*path, op); - } + this->recordClip(*path, op); + mCanvas->clipPath(*path, op); return !mCanvas->isClipEmpty(); } diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp index ee202367d73e..773a7ea54a52 100644 --- a/libs/hwui/tests/unit/FontRendererTests.cpp +++ b/libs/hwui/tests/unit/FontRendererTests.cpp @@ -28,7 +28,7 @@ static bool isZero(uint8_t* data, int size) { return true; } -RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, DISABLED_renderDropShadow) { SkPaint paint; paint.setTextSize(10); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index 7ae58a68a76c..dafa0745ad67 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -20,6 +20,7 @@ #include <SkColorMatrixFilter.h> #include <SkColorSpace.h> #include <SkImagePriv.h> +#include <SkPathOps.h> #include <SkShader.h> using namespace android; @@ -90,6 +91,16 @@ TEST(SkiaBehavior, porterDuffCreateIsCached) { ASSERT_EQ(expected, paint.getBlendMode()); } +TEST(SkiaBehavior, pathIntersection) { + SkPath p0, p1, result; + p0.addRect(SkRect::MakeXYWH(-5.0f, 0.0f, 1080.0f, 242.0f)); + p1.addRect(SkRect::MakeXYWH(0.0f, 0.0f, 1080.0f, 242.0f)); + Op(p0, p1, kIntersect_SkPathOp, &result); + SkRect resultRect; + ASSERT_TRUE(result.isRect(&resultRect)); + ASSERT_EQ(SkRect::MakeXYWH(0.0f, 0.0f, 1075.0f, 242.0f), resultRect); +} + TEST(SkiaBehavior, srgbColorSpaceIsSingleton) { sk_sp<SkColorSpace> sRGB1 = SkColorSpace::MakeSRGB(); sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeSRGB(); diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp index 8312bda8d67d..5383e5756b93 100644 --- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp +++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp @@ -26,7 +26,7 @@ using namespace android; using namespace android::uirenderer; -RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, DISABLED_addRemove) { SkPaint paint; paint.setTextSize(20); diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index b222a6da9a98..68f46ad27750 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -646,7 +646,10 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasAltitude} will return false, * and {@link #getAltitude} will return 0.0. + * + * @deprecated use a new Location object for location updates. */ + @Deprecated public void removeAltitude() { mAltitude = 0.0f; mFieldsMask &= ~HAS_ALTITUDE_MASK; @@ -683,7 +686,10 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasSpeed} will return false, * and {@link #getSpeed} will return 0.0. + * + * @deprecated use a new Location object for location updates. */ + @Deprecated public void removeSpeed() { mSpeed = 0.0f; mFieldsMask &= ~HAS_SPEED_MASK; @@ -733,7 +739,10 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasBearing} will return false, * and {@link #getBearing} will return 0.0. + * + * @deprecated use a new Location object for location updates. */ + @Deprecated public void removeBearing() { mBearing = 0.0f; mFieldsMask &= ~HAS_BEARING_MASK; @@ -790,7 +799,10 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasAccuracy} will return false, and * {@link #getAccuracy} will return 0.0. + * + * @deprecated use a new Location object for location updates. */ + @Deprecated public void removeAccuracy() { mHorizontalAccuracyMeters = 0.0f; mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK; @@ -839,7 +851,11 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasVerticalAccuracy} will return false, and * {@link #getVerticalAccuracyMeters} will return 0.0. + * + * @deprecated use a new Location object for location updates. + * @removed */ + @Deprecated public void removeVerticalAccuracy() { mVerticalAccuracyMeters = 0.0f; mFieldsMask &= ~HAS_VERTICAL_ACCURACY_MASK; @@ -883,7 +899,11 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasSpeedAccuracy} will return false, and * {@link #getSpeedAccuracyMetersPerSecond} will return 0.0. + * + * @deprecated use a new Location object for location updates. + * @removed */ + @Deprecated public void removeSpeedAccuracy() { mSpeedAccuracyMetersPerSecond = 0.0f; mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK; @@ -927,7 +947,11 @@ public class Location implements Parcelable { * * <p>Following this call {@link #hasBearingAccuracy} will return false, and * {@link #getBearingAccuracyDegrees} will return 0.0. + * + * @deprecated use a new Location object for location updates. + * @removed */ + @Deprecated public void removeBearingAccuracy() { mBearingAccuracyDegrees = 0.0f; mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK; diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 9386246bd128..d5efc971d1fd 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1007,13 +1007,14 @@ public class MediaPlayer extends PlayerBase * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @param headers the headers to be sent together with the request for the data - * Note that the cross domain redirection is allowed by default, but that can be - * changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value - * to disallow or allow cross domain redirection. * The headers must not include cookies. Instead, use the cookies param. * @param cookies the cookies to be sent together with the request * @throws IllegalStateException if it is called in an invalid state + * + * <p><strong>Note</strong> that the cross domain redirection is allowed by default, + * but that can be changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to + * disallow or allow cross domain redirection. */ public void setDataSource(@NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) @@ -1056,11 +1057,12 @@ public class MediaPlayer extends PlayerBase * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @param headers the headers to be sent together with the request for the data - * Note that the cross domain redirection is allowed by default, but that can be - * changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value - * to disallow or allow cross domain redirection. * @throws IllegalStateException if it is called in an invalid state + * + * <p><strong>Note</strong> that the cross domain redirection is allowed by default, + * but that can be changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to + * disallow or allow cross domain redirection. */ public void setDataSource(@NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers) @@ -1981,7 +1983,7 @@ public class MediaPlayer extends PlayerBase mOnSubtitleDataListener = null; // Modular DRM clean up - mOnDrmConfigListener = null; + mOnDrmConfigHelper = null; mOnDrmInfoHandlerDelegate = null; mOnDrmPreparedHandlerDelegate = null; resetDrmState(); @@ -3905,11 +3907,11 @@ public class MediaPlayer extends PlayerBase * 'securityLevel', which has to be set after DRM scheme creation but * before the DRM session is opened. * - * The only allowed DRM calls in this listener are getDrmPropertyString - * and setDrmPropertyString. + * The only allowed DRM calls in this listener are {@code getDrmPropertyString} + * and {@code setDrmPropertyString}. * */ - public interface OnDrmConfigListener + public interface OnDrmConfigHelper { /** * Called to give the app the opportunity to configure DRM before the session is created @@ -3922,19 +3924,19 @@ public class MediaPlayer extends PlayerBase /** * Register a callback to be invoked for configuration of the DRM object before * the session is created. - * The callback will be invoked synchronously half-way into the execution + * The callback will be invoked synchronously during the execution * of {@link #prepareDrm(UUID uuid)}. * * @param listener the callback that will be run */ - public void setOnDrmConfigListener(OnDrmConfigListener listener) + public void setOnDrmConfigHelper(OnDrmConfigHelper listener) { synchronized (mDrmLock) { - mOnDrmConfigListener = listener; + mOnDrmConfigHelper = listener; } // synchronized } - private OnDrmConfigListener mOnDrmConfigListener; + private OnDrmConfigHelper mOnDrmConfigHelper; /** * Interface definition of a callback to be invoked when the @@ -3946,7 +3948,7 @@ public class MediaPlayer extends PlayerBase * Called to indicate DRM info is available * * @param mp the {@code MediaPlayer} associated with this callback - * @param drmInfo DRM info of the source including PSSH, mimes, and subset + * @param drmInfo DRM info of the source including PSSH, and subset * of crypto schemes supported by this device */ public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo); @@ -3982,6 +3984,41 @@ public class MediaPlayer extends PlayerBase private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate; + + /** + * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener. + * <p> + * + * DRM preparation has succeeded. + */ + public static final int PREPARE_DRM_STATUS_SUCCESS = 0; + + /** + * The device required DRM provisioning but couldn't reach the provisioning server. + */ + public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; + + /** + * The device required DRM provisioning but the provisioning server denied the request. + */ + public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; + + /** + * The DRM preparation has failed . + */ + public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; + + + /** @hide */ + @IntDef({ + PREPARE_DRM_STATUS_SUCCESS, + PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR, + PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR, + PREPARE_DRM_STATUS_PREPARATION_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrepareDrmStatusCode {} + /** * Interface definition of a callback to notify the app when the * DRM is ready for key request/response @@ -3992,9 +4029,13 @@ public class MediaPlayer extends PlayerBase * Called to notify the app that prepareDrm is finished and ready for key request/response * * @param mp the {@code MediaPlayer} associated with this callback - * @param success the result of DRM preparation + * @param status the result of DRM preparation which can be + * {@link #PREPARE_DRM_STATUS_SUCCESS}, + * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR}, + * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or + * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}. */ - public void onDrmPrepared(MediaPlayer mp, boolean success); + public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status); } /** @@ -4038,30 +4079,28 @@ public class MediaPlayer extends PlayerBase mOnDrmInfoListener = listener; // find the looper for our new event handler - Looper looper = null; if (handler != null) { - looper = handler.getLooper(); - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - public void handleMessage(Message msg) { - DrmInfo drmInfo = (DrmInfo)msg.obj; - mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); - } - }; + mHandler = handler; + } else { + // handler == null + // Will let OnDrmInfoListener be called in mEventHandler similar to other + // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be + // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by + // mediaserver). As a result, the callback has to be called directly by + // EventHandler.handleMessage similar to onPrepared. } } void notifyClient(DrmInfo drmInfo) { - if ( mHandler != null ) { - Message msg = new Message(); // no message type needed - msg.obj = drmInfo; - mHandler.sendMessage(msg); + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); + } + }); } - else { // no handler: direct call + else { // no handler: direct call by mEventHandler mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); } } @@ -4078,31 +4117,26 @@ public class MediaPlayer extends PlayerBase mOnDrmPreparedListener = listener; // find the looper for our new event handler - Looper looper = null; if (handler != null) { - looper = handler.getLooper(); - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - public void handleMessage(Message msg) { - boolean success = (msg.arg1 == 0) ? false : true; - mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success); - } - }; + mHandler = handler; + } else if (mEventHandler != null) { + // Otherwise, use mEventHandler + mHandler = mEventHandler; + } else { + Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler"); } } - void notifyClient(boolean success) { - if ( mHandler != null ) { - Message msg = new Message(); // no message type needed - msg.arg1 = success ? 1 : 0; - mHandler.sendMessage(msg); - } - else { // no handler: direct call - mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success); + void notifyClient(int status) { + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status); + } + }); + } else { + Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler"); } } } @@ -4137,7 +4171,7 @@ public class MediaPlayer extends PlayerBase /** * Prepares the DRM for the current source * <p> - * If {@code OnDrmConfigListener} is registered, it will be called half-way into + * If {@code OnDrmConfigHelper} is registered, it will be called during * preparation to allow configuration of the DRM properties before opening the * DRM session. Note that the callback is called synchronously in the thread that called * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString} @@ -4148,9 +4182,9 @@ public class MediaPlayer extends PlayerBase * complete depending on the network connectivity. * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking * mode by launching the provisioning in the background and returning. The listener - * will be called when provisioning and preperation has finished. If a + * will be called when provisioning and preparation has finished. If a * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning - * and preperation has finished, i.e., runs in blocking mode. + * and preparation has finished, i.e., runs in blocking mode. * <p> * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM * session being ready. The application should not make any assumption about its call @@ -4158,18 +4192,23 @@ public class MediaPlayer extends PlayerBase * execute the listener (unless the listener is registered with a handler thread). * <p> * - * @param uuid The UUID of the crypto scheme. + * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved + * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}. * - * @throws IllegalStateException if called before prepare(), or there exists a Drm already - * @throws UnsupportedSchemeException if the crypto scheme is not supported - * @throws ResourceBusyException if required DRM resources are in use - * @throws ProvisioningErrorException if provisioning is required but an attempt failed + * @throws IllegalStateException if called before prepare(), or the DRM was + * prepared already + * @throws UnsupportedSchemeException if the crypto scheme is not supported + * @throws ResourceBusyException if required DRM resources are in use + * @throws ProvisioningNetworkErrorException if provisioning is required but failed due to a + * network error + * @throws ProvisioningServerErrorException if provisioning is required but failed due to + * the request denied by the provisioning server */ public void prepareDrm(@NonNull UUID uuid) - throws UnsupportedSchemeException, - ResourceBusyException, ProvisioningErrorException + throws UnsupportedSchemeException, ResourceBusyException, + ProvisioningNetworkErrorException, ProvisioningServerErrorException { - Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigListener: " + mOnDrmConfigListener); + Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper); boolean allDoneWithoutProvisioning = false; // get a snapshot as we'll use them outside the lock @@ -4177,7 +4216,7 @@ public class MediaPlayer extends PlayerBase synchronized (mDrmLock) { - // only allowing if tied to a protected source; might releax for releasing offline keys + // only allowing if tied to a protected source; might relax for releasing offline keys if (mDrmInfo == null) { final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " + "DRM info be retrieved before this call."; @@ -4226,8 +4265,8 @@ public class MediaPlayer extends PlayerBase // call the callback outside the lock - if (mOnDrmConfigListener != null) { - mOnDrmConfigListener.onDrmConfig(this); + if (mOnDrmConfigHelper != null) { + mOnDrmConfigHelper.onDrmConfig(this); } synchronized (mDrmLock) { @@ -4251,15 +4290,33 @@ public class MediaPlayer extends PlayerBase Log.w(TAG, "prepareDrm: NotProvisionedException"); // handle provisioning internally; it'll reset mPrepareDrmInProgress - boolean result = HandleProvisioninig(uuid); + int result = HandleProvisioninig(uuid); // if blocking mode, we're already done; // if non-blocking mode, we attempted to launch background provisioning - if (result == false) { - final String msg = "prepareDrm: Provisioning was required but failed."; - Log.e(TAG, msg); + if (result != PREPARE_DRM_STATUS_SUCCESS) { earlyExit = true; - throw new ProvisioningErrorException(msg); + String msg; + + switch (result) { + case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR: + msg = "prepareDrm: Provisioning was required but failed " + + "due to a network error."; + Log.e(TAG, msg); + throw new ProvisioningNetworkErrorException(msg); + + case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR: + msg = "prepareDrm: Provisioning was required but the request " + + "was denied by the server."; + Log.e(TAG, msg); + throw new ProvisioningServerErrorException(msg); + + case PREPARE_DRM_STATUS_PREPARATION_ERROR: + default: // default for safeguard + msg = "prepareDrm: Post-provisioning preparation failed."; + Log.e(TAG, msg); + throw new IllegalStateException(msg); + } } // nothing else to do; // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup @@ -4281,7 +4338,7 @@ public class MediaPlayer extends PlayerBase // if finished successfully without provisioning, call the callback outside the lock if (allDoneWithoutProvisioning) { if (onDrmPreparedHandlerDelegate != null) - onDrmPreparedHandlerDelegate.notifyClient(true /*success*/); + onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS); } } @@ -4291,6 +4348,10 @@ public class MediaPlayer extends PlayerBase /** * Releases the DRM session + * <p> + * The player has to have an active DRM session and be in stopped, or prepared + * state before this call is made. + * A {@code reset()} call will release the DRM session implicitly. * * @throws NoDrmSchemeException if there is no active DRM session to release */ @@ -4307,7 +4368,7 @@ public class MediaPlayer extends PlayerBase try { // we don't have the player's state in this layer. The below call raises - // exception if we're in a non-stopped/idle state. + // exception if we're in a non-stopped/prepared state. // for cleaning native/mediaserver crypto object _releaseDrm(); @@ -4316,9 +4377,11 @@ public class MediaPlayer extends PlayerBase cleanDrmObj(); mActiveDrmScheme = false; - } catch (Exception e) { + } catch (IllegalStateException e) { Log.w(TAG, "releaseDrm: Exception ", e); - throw e; + throw new IllegalStateException("releaseDrm: The player is not in a valid state."); + } catch (Exception e) { + Log.e(TAG, "releaseDrm: Exception ", e); } } // synchronized } @@ -4337,21 +4400,23 @@ public class MediaPlayer extends PlayerBase * it should deliver to the response to the DRM engine plugin using the method * {@link #provideKeyResponse}. * - * @param scope may be a container-specific initialization data or a keySetId, - * depending on the specified keyType. - * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set to - * the container-specific initialization data. Its meaning is interpreted based on the - * mime type provided in the mimeType parameter. It could contain, for example, - * the content ID, key ID or other data obtained from the content metadata that is - * required in generating the key request. - * When the keyType is KEY_TYPE_RELEASE, scope should be set to the keySetId of - * the keys being released. + * @param keySetId is the key-set identifier of the offline keys being released when keyType is + * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when + * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. + * + * @param initData is the container-specific initialization data when the keyType is + * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is + * interpreted based on the mime type provided in the mimeType parameter. It could + * contain, for example, the content ID, key ID or other data obtained from the content + * metadata that is required in generating the key request. + * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null. * * @param mimeType identifies the mime type of the content * - * @param keyType specifes the type of the request. The request may be to acquire - * keys for streaming or offline content, or to release previously acquired - * keys, which are identified by a keySetId. + * @param keyType specifies the type of the request. The request may be to acquire + * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content + * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired + * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId. * * @param optionalParameters are included in the key request message to * allow a client application to provide additional message parameters to the server. @@ -4360,12 +4425,13 @@ public class MediaPlayer extends PlayerBase * @throws NoDrmSchemeException if there is no active DRM session */ @NonNull - public MediaDrm.KeyRequest getKeyRequest(@NonNull byte[] scope, @Nullable String mimeType, - @MediaDrm.KeyType int keyType, @Nullable Map<String, String> optionalParameters) + public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData, + @Nullable String mimeType, @MediaDrm.KeyType int keyType, + @Nullable Map<String, String> optionalParameters) throws NoDrmSchemeException { Log.v(TAG, "getKeyRequest: " + - " scope: " + scope + " mimeType: " + mimeType + + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType + " keyType: " + keyType + " optionalParameters: " + optionalParameters); synchronized (mDrmLock) { @@ -4375,20 +4441,16 @@ public class MediaPlayer extends PlayerBase } try { - byte[] scopeOut = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? - mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE - scope; // keySetId for KEY_TYPE_RELEASE - - byte[] initData = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? - scope : // initData for KEY_TYPE_STREAMING/OFFLINE - null; // not used for KEY_TYPE_RELEASE + byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? + mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE + keySetId; // keySetId for KEY_TYPE_RELEASE HashMap<String, String> hmapOptionalParameters = (optionalParameters != null) ? new HashMap<String, String>(optionalParameters) : null; - MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scopeOut, initData, mimeType, + MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType, keyType, hmapOptionalParameters); Log.v(TAG, "getKeyRequest: --> request: " + request); @@ -4499,8 +4561,8 @@ public class MediaPlayer extends PlayerBase * @param propertyName the property name * * Standard fields names are: - * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION}, - * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS} + * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, + * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} */ @NonNull public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName) @@ -4537,8 +4599,8 @@ public class MediaPlayer extends PlayerBase * @param value the property value * * Standard fields names are: - * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION}, - * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS} + * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, + * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} */ public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value) @@ -4565,8 +4627,6 @@ public class MediaPlayer extends PlayerBase public static final class DrmInfo { private Map<UUID, byte[]> mapPssh; private UUID[] supportedSchemes; - // TODO: Won't need this in final release. Only keeping it for the existing test app. - private String[] mimes; public Map<UUID, byte[]> getPssh() { return mapPssh; @@ -4574,15 +4634,10 @@ public class MediaPlayer extends PlayerBase public UUID[] getSupportedSchemes() { return supportedSchemes; } - // TODO: Won't need this in final release. Only keeping it for the existing test app. - public String[] getMimes() { - return mimes; - } - private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes, String[] Mimes) { + private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) { mapPssh = Pssh; supportedSchemes = SupportedSchemes; - mimes = Mimes; } private DrmInfo(Parcel parcel) { @@ -4608,18 +4663,12 @@ public class MediaPlayer extends PlayerBase supportedSchemes[i]); } - // TODO: Won't need this in final release. Only keeping it for the test app. - mimes = parcel.readStringArray(); - int mimeCount = mimes.length; - Log.v(TAG, "DrmInfo() mime: " + Arrays.toString(mimes)); - Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize + - " supportedDRMsCount: " + supportedDRMsCount + - " mimeCount: " + mimeCount); + " supportedDRMsCount: " + supportedDRMsCount); } private DrmInfo makeCopy() { - return new DrmInfo(this.mapPssh, this.supportedSchemes, this.mimes); + return new DrmInfo(this.mapPssh, this.supportedSchemes); } private String arrToHex(byte[] bytes) { @@ -4714,11 +4763,22 @@ public class MediaPlayer extends PlayerBase /** * Thrown when the device requires DRM provisioning but the provisioning attempt has - * failed (for example: network timeout, provisioning server error). + * failed due to a network error (Internet reachability, timeout, etc.). * Extends MediaDrm.MediaDrmException */ - public static final class ProvisioningErrorException extends MediaDrmException { - public ProvisioningErrorException(String detailMessage) { + public static final class ProvisioningNetworkErrorException extends MediaDrmException { + public ProvisioningNetworkErrorException(String detailMessage) { + super(detailMessage); + } + } + + /** + * Thrown when the device requires DRM provisioning but the provisioning attempt has + * failed due to the provisioning server denying the request. + * Extends MediaDrm.MediaDrmException + */ + public static final class ProvisioningServerErrorException extends MediaDrmException { + public ProvisioningServerErrorException(String detailMessage) { super(detailMessage); } } @@ -4770,14 +4830,13 @@ public class MediaPlayer extends PlayerBase private UUID uuid; private String urlStr; - private byte[] response; private Object drmLock; private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate; private MediaPlayer mediaPlayer; - private boolean succeeded; + private int status; private boolean finished; - public boolean succeeded() { - return succeeded; + public int status() { + return status; } public ProvisioningThread initialize(MediaDrm.ProvisionRequest request, @@ -4790,12 +4849,15 @@ public class MediaPlayer extends PlayerBase urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); this.uuid = uuid; + status = PREPARE_DRM_STATUS_PREPARATION_ERROR; + Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr); return this; } public void run() { + byte[] response = null; boolean provisioningSucceeded = false; try { URL url = new URL(urlStr); @@ -4813,11 +4875,13 @@ public class MediaPlayer extends PlayerBase Log.v(TAG, "HandleProvisioninig: Thread run: response " + response.length + " " + response); } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url); } finally { connection.disconnect(); } } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e); } @@ -4828,12 +4892,15 @@ public class MediaPlayer extends PlayerBase "provideProvisionResponse SUCCEEDED!"); provisioningSucceeded = true; - } catch (Exception e) { + } catch (Exception e) { + status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR; Log.w(TAG, "HandleProvisioninig: Thread run: " + "provideProvisionResponse " + e); } } + boolean succeeded = false; + // non-blocking mode needs the lock if (onDrmPreparedHandlerDelegate != null) { @@ -4841,6 +4908,9 @@ public class MediaPlayer extends PlayerBase // continuing with prepareDrm if (provisioningSucceeded) { succeeded = mediaPlayer.resumePrepareDrm(uuid); + status = (succeeded) ? + PREPARE_DRM_STATUS_SUCCESS : + PREPARE_DRM_STATUS_PREPARATION_ERROR; } mediaPlayer.mDrmProvisioningInProgress = false; mediaPlayer.mPrepareDrmInProgress = false; @@ -4850,12 +4920,15 @@ public class MediaPlayer extends PlayerBase } // synchronized // calling the callback outside the lock - onDrmPreparedHandlerDelegate.notifyClient(succeeded); + onDrmPreparedHandlerDelegate.notifyClient(status); } else { // blocking mode already has the lock // continuing with prepareDrm if (provisioningSucceeded) { succeeded = mediaPlayer.resumePrepareDrm(uuid); + status = (succeeded) ? + PREPARE_DRM_STATUS_SUCCESS : + PREPARE_DRM_STATUS_PREPARATION_ERROR; } mediaPlayer.mDrmProvisioningInProgress = false; mediaPlayer.mPrepareDrmInProgress = false; @@ -4869,19 +4942,19 @@ public class MediaPlayer extends PlayerBase } // ProvisioningThread - private boolean HandleProvisioninig(UUID uuid) + private int HandleProvisioninig(UUID uuid) { // the lock is already held by the caller if (mDrmProvisioningInProgress) { Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress"); - return false; + return PREPARE_DRM_STATUS_PREPARATION_ERROR; } MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); if (provReq == null) { Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null."); - return false; + return PREPARE_DRM_STATUS_PREPARATION_ERROR; } Log.v(TAG, "HandleProvisioninig provReq " + @@ -4893,11 +4966,11 @@ public class MediaPlayer extends PlayerBase mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this); mDrmProvisioningThread.start(); - boolean result = false; + int result; - // non-blocking + // non-blocking: this is not the final result if (mOnDrmPreparedHandlerDelegate != null) { - result = true; + result = PREPARE_DRM_STATUS_SUCCESS; } else { // if blocking mode, wait till provisioning is done try { @@ -4905,7 +4978,7 @@ public class MediaPlayer extends PlayerBase } catch (Exception e) { Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e); } - result = mDrmProvisioningThread.succeeded(); + result = mDrmProvisioningThread.status(); // no longer need the thread mDrmProvisioningThread = null; } diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index 5bf205e12799..789d5e0d8478 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -365,7 +365,7 @@ public final class MediaBrowser { * @param parentId The id of the parent media item whose list of children * will be subscribed. * @param options The bundle of service-specific arguments to send to the media - * browse service. The contents of this bundle may affect the + * browser service. The contents of this bundle may affect the * information returned when browsing. * @param callback The callback to receive the list of children. */ @@ -453,7 +453,7 @@ public final class MediaBrowser { try { mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks); } catch (RemoteException e) { - Log.i(TAG, "Remote error getting media item.", e); + Log.i(TAG, "Remote error getting media item."); mHandler.post(new Runnable() { @Override public void run() { @@ -463,62 +463,6 @@ public final class MediaBrowser { } } - /** - * Searches {@link MediaItem media items} from the connected service. Not - * all services may support this, and {@link SearchCallback#onError} will be - * called if not implemented. - * - * @param query The search query that contains keywords separated by space. Should not be - * an empty string. - * @param extras The bundle of service-specific arguments to send to the media browser - * service. The contents of this bundle may affect the search result. - * @param callback The callback to receive the search result. - * @throws IllegalStateException if the browser is not connected to the media browser service. - */ - public void search(@NonNull final String query, final Bundle extras, SearchCallback callback) { - if (TextUtils.isEmpty(query)) { - throw new IllegalArgumentException("query cannot be empty."); - } - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null."); - } - if (mState != CONNECT_STATE_CONNECTED) { - throw new IllegalStateException("search() called while not connected (state=" - + getStateLabel(mState) + ")"); - } - ResultReceiver receiver = new ResultReceiver(mHandler) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode != 0 || resultData == null - || !resultData.containsKey(MediaBrowserService.KEY_SEARCH_RESULTS)) { - callback.onError(query, extras); - return; - } - Parcelable[] items = resultData.getParcelableArray( - MediaBrowserService.KEY_SEARCH_RESULTS); - List<MediaItem> results = null; - if (items != null) { - results = new ArrayList<>(); - for (Parcelable item : items) { - results.add((MediaItem) item); - } - } - callback.onSearchResult(query, extras, results); - } - }; - try { - mServiceBinder.search(query, extras, receiver, mServiceCallbacks); - } catch (RemoteException e) { - Log.i(TAG, "Remote error getting media item.", e); - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onError(query, extras); - } - }); - } - } - private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) { // Check arguments. if (TextUtils.isEmpty(parentId)) { @@ -945,7 +889,7 @@ public final class MediaBrowser { * @param parentId The media id of the parent media item. * @param children The children which were loaded. * @param options The bundle of service-specific arguments sent to the media - * browse service. The contents of this bundle may affect the + * browser service. The contents of this bundle may affect the * information returned when browsing. */ public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children, @@ -1004,32 +948,6 @@ public final class MediaBrowser { } /** - * Callback for receiving the result of {@link #search}. - */ - public static abstract class SearchCallback { - /** - * Called when the {@link #search} finished successfully. - * - * @param query The search query sent for the search request to the connected service. - * @param extras The bundle of service-specific arguments sent to the connected service. - * @param items The list of media items which contains the search result. - */ - public void onSearchResult(@NonNull String query, Bundle extras, - @NonNull List<MediaItem> items) { - } - - /** - * Called when an error happens while {@link #search} or the connected service doesn't - * support {@link #search}. - * - * @param query The search query sent for the search request to the connected service. - * @param extras The bundle of service-specific arguments sent to the connected service. - */ - public void onError(@NonNull String query, Bundle extras) { - } - } - - /** * ServiceConnection to the other app. */ private class MediaServiceConnection implements ServiceConnection { diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 4f3314c1e8ab..3affee5c0aa7 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -45,8 +45,6 @@ interface ISession { void setQueueTitle(CharSequence title); void setExtras(in Bundle extras); void setRatingType(int type); - void setRepeatMode(int repeatMode); - void setShuffleModeEnabled(boolean enabled); // These commands relate to volume handling void setPlaybackToLocal(in AudioAttributes attributes); diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index a146c6226171..893bd3c8cbb1 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -16,7 +16,6 @@ package android.media.session; import android.content.Intent; -import android.media.MediaDescription; import android.media.Rating; import android.net.Uri; import android.os.Bundle; @@ -47,13 +46,7 @@ oneway interface ISessionCallback { void onRewind(); void onSeekTo(long pos); void onRate(in Rating rating); - void onRepeatMode(int repeatMode); - void onShuffleMode(boolean enabled); void onCustomAction(String action, in Bundle args); - void onAddQueueItem(in MediaDescription description); - void onAddQueueItemAt(in MediaDescription description, int index); - void onRemoveQueueItem(in MediaDescription description); - void onRemoveQueueItemAt(int index); // These callbacks are for volume handling void onAdjustVolume(int direction); diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 7b5233ac0704..249bcdc802ce 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -18,7 +18,6 @@ package android.media.session; import android.app.PendingIntent; import android.content.Intent; import android.content.pm.ParceledListSlice; -import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.session.ISessionControllerCallback; @@ -49,19 +48,6 @@ interface ISessionController { ParcelableVolumeInfo getVolumeAttributes(); void adjustVolume(int direction, int flags, String packageName); void setVolumeTo(int value, int flags, String packageName); - MediaMetadata getMetadata(); - PlaybackState getPlaybackState(); - ParceledListSlice getQueue(); - void addQueueItem(in MediaDescription description); - void addQueueItemAt(in MediaDescription description, int index); - void removeQueueItem(in MediaDescription description); - void removeQueueItemAt(int index); - - CharSequence getQueueTitle(); - Bundle getExtras(); - int getRatingType(); - int getRepeatMode(); - boolean isShuffleModeEnabled(); // These commands are for the TransportControls void prepare(); @@ -81,7 +67,11 @@ interface ISessionController { void rewind(); void seekTo(long pos); void rate(in Rating rating); - void repeatMode(int repeatMode); - void shuffleMode(boolean enabled); void sendCustomAction(String action, in Bundle args); + MediaMetadata getMetadata(); + PlaybackState getPlaybackState(); + ParceledListSlice getQueue(); + CharSequence getQueueTitle(); + Bundle getExtras(); + int getRatingType(); } diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl index 9517c088bcf3..cf3176706d7e 100644 --- a/media/java/android/media/session/ISessionControllerCallback.aidl +++ b/media/java/android/media/session/ISessionControllerCallback.aidl @@ -36,6 +36,4 @@ oneway interface ISessionControllerCallback { void onQueueTitleChanged(CharSequence title); void onExtrasChanged(in Bundle extras); void onVolumeInfoChanged(in ParcelableVolumeInfo info); - void onRepeatModeChanged(int repeatMode); - void onShuffleModeChanged(boolean shuffleMode); } diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index bab2af25dde5..622900f5c7f9 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.AudioManager; -import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; @@ -39,7 +38,6 @@ import android.util.Log; import android.view.KeyEvent; import java.lang.ref.WeakReference; -import java.lang.UnsupportedOperationException; import java.util.ArrayList; import java.util.List; @@ -65,9 +63,7 @@ public final class MediaController { private static final int MSG_UPDATE_QUEUE = 5; private static final int MSG_UPDATE_QUEUE_TITLE = 6; private static final int MSG_UPDATE_EXTRAS = 7; - private static final int MSG_UPDATE_REPEAT_MODE = 8; - private static final int MSG_UPDATE_SHUFFLE_MODE = 9; - private static final int MSG_DESTROYED = 10; + private static final int MSG_DESTROYED = 8; private final ISessionController mSessionBinder; @@ -113,7 +109,8 @@ public final class MediaController { } /** - * Get a {@link TransportControls} instance to send transport actions to this session. + * Get a {@link TransportControls} instance to send transport actions to + * the associated session. * * @return A transport controls instance. */ @@ -152,7 +149,7 @@ public final class MediaController { try { return mSessionBinder.getPlaybackState(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPlaybackState", e); + Log.wtf(TAG, "Error calling getPlaybackState.", e); return null; } } @@ -166,7 +163,7 @@ public final class MediaController { try { return mSessionBinder.getMetadata(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getMetadata", e); + Log.wtf(TAG, "Error calling getMetadata.", e); return null; } } @@ -184,103 +181,12 @@ public final class MediaController { return queue.getList(); } } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getQueue", e); + Log.wtf(TAG, "Error calling getQueue.", e); } return null; } /** - * Add a queue item from the given {@code description} at the end of the play queue - * of this session. Not all sessions may support this. - * - * @param description The {@link MediaDescription} for creating the - * {@link MediaSession.QueueItem} to be inserted. - * @throws UnsupportedOperationException If this session doesn't support this. - * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS - */ - public void addQueueItem(MediaDescription description) { - try { - long flags = mSessionBinder.getFlags(); - if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { - throw new UnsupportedOperationException( - "This session doesn't support queue management operations"); - } - mSessionBinder.addQueueItem(description); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling addQueueItem", e); - } - } - - /** - * Add a queue item from the given {@code description} at the specified position - * in the play queue of this session. Shifts the queue item currently at that position - * (if any) and any subsequent queue items to the right (adds one to their indices). - * Not all sessions may support this. - * - * @param description The {@link MediaDescription} for creating the - * {@link MediaSession.QueueItem} to be inserted. - * @param index The index at which the created {@link MediaSession.QueueItem} is to be inserted. - * @throws UnsupportedOperationException If this session doesn't support this. - * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS - */ - public void addQueueItem(MediaDescription description, int index) { - try { - long flags = mSessionBinder.getFlags(); - if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { - throw new UnsupportedOperationException( - "This session doesn't support queue management operations"); - } - mSessionBinder.addQueueItemAt(description, index); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling addQueueItemAt", e); - } - } - - /** - * Remove the first occurrence of the specified {@link MediaSession.QueueItem} - * with the given {@link MediaDescription description} in the play queue of the associated - * session. Not all sessions may support this. - * - * @param description The {@link MediaDescription} for denoting the - * {@link MediaSession.QueueItem} to be removed. - * @throws UnsupportedOperationException If this session doesn't support this. - * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS - */ - public void removeQueueItem(MediaDescription description) { - try { - long flags = mSessionBinder.getFlags(); - if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { - throw new UnsupportedOperationException( - "This session doesn't support queue management operations"); - } - mSessionBinder.removeQueueItem(description); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling removeQueueItem", e); - } - } - - /** - * Remove an queue item at the specified position in the play queue - * of this session. Not all sessions may support this. - * - * @param index The index of the element to be removed. - * @throws UnsupportedOperationException If this session doesn't support this. - * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS - */ - public void removeQueueItemAt(int index) { - try { - long flags = mSessionBinder.getFlags(); - if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { - throw new UnsupportedOperationException( - "This session doesn't support queue management operations"); - } - mSessionBinder.removeQueueItemAt(index); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling removeQueueItemAt", e); - } - } - - /** * Get the queue title for this session. */ public @Nullable CharSequence getQueueTitle() { @@ -322,41 +228,12 @@ public final class MediaController { try { return mSessionBinder.getRatingType(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getRatingType", e); + Log.wtf(TAG, "Error calling getRatingType.", e); return Rating.RATING_NONE; } } /** - * Get the repeat mode for this session. - * - * @return The latest repeat mode set to the session, or - * {@link PlaybackState#REPEAT_MODE_NONE} if not set. - */ - public int getRepeatMode() { - try { - return mSessionBinder.getRepeatMode(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getRepeatMode", e); - return PlaybackState.REPEAT_MODE_NONE; - } - } - - /** - * Return whether the shuffle mode is enabled for this session. - * - * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set. - */ - public boolean isShuffleModeEnabled() { - try { - return mSessionBinder.isShuffleModeEnabled(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling isShuffleModeEnabled", e); - return false; - } - } - - /** * Get the flags for this session. Flags are defined in {@link MediaSession}. * * @return The current set of flags for the session. @@ -365,7 +242,7 @@ public final class MediaController { try { return mSessionBinder.getFlags(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getFlags", e); + Log.wtf(TAG, "Error calling getFlags.", e); } return 0; } @@ -382,7 +259,7 @@ public final class MediaController { result.maxVolume, result.currentVolume); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getAudioInfo", e); + Log.wtf(TAG, "Error calling getAudioInfo.", e); } return null; } @@ -397,7 +274,7 @@ public final class MediaController { try { return mSessionBinder.getLaunchPendingIntent(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPendingIntent", e); + Log.wtf(TAG, "Error calling getPendingIntent.", e); } return null; } @@ -426,7 +303,7 @@ public final class MediaController { try { mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName()); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setVolumeTo", e); + Log.wtf(TAG, "Error calling setVolumeTo.", e); } } @@ -447,7 +324,7 @@ public final class MediaController { try { mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName()); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy", e); + Log.wtf(TAG, "Error calling adjustVolumeBy.", e); } } @@ -513,7 +390,7 @@ public final class MediaController { try { mSessionBinder.sendCommand(command, args, cb); } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCommand", e); + Log.d(TAG, "Dead object in sendCommand.", e); } } @@ -527,7 +404,7 @@ public final class MediaController { try { mPackageName = mSessionBinder.getPackageName(); } catch (RemoteException e) { - Log.d(TAG, "Dead object in getPackageName", e); + Log.d(TAG, "Dead object in getPackageName.", e); } } return mPackageName; @@ -544,7 +421,7 @@ public final class MediaController { try { mTag = mSessionBinder.getTag(); } catch (RemoteException e) { - Log.d(TAG, "Dead object in getTag", e); + Log.d(TAG, "Dead object in getTag.", e); } } return mTag; @@ -702,25 +579,6 @@ public final class MediaController { */ public void onAudioInfoChanged(PlaybackInfo info) { } - - /** - * Override to handle changes to the repeat mode. - * - * @param repeatMode The repeat mode. It should be one of followings: - * {@link PlaybackState#REPEAT_MODE_NONE}, - * {@link PlaybackState#REPEAT_MODE_ONE}, - * {@link PlaybackState#REPEAT_MODE_ALL} - */ - public void onRepeatModeChanged(@PlaybackState.RepeatMode int repeatMode) { - } - - /** - * Override to handle changes to the shuffle mode. - * - * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise. - */ - public void onShuffleModeChanged(boolean enabled) { - } } /** @@ -744,7 +602,7 @@ public final class MediaController { try { mSessionBinder.prepare(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare", e); + Log.wtf(TAG, "Error calling prepare.", e); } } @@ -763,12 +621,12 @@ public final class MediaController { public void prepareFromMediaId(String mediaId, Bundle extras) { if (TextUtils.isEmpty(mediaId)) { throw new IllegalArgumentException( - "You must specify a non-empty String for prepareFromMediaId"); + "You must specify a non-empty String for prepareFromMediaId."); } try { mSessionBinder.prepareFromMediaId(mediaId, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + mediaId + ")", e); + Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); } } @@ -794,7 +652,7 @@ public final class MediaController { try { mSessionBinder.prepareFromSearch(query, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + query + ")", e); + Log.wtf(TAG, "Error calling prepare(" + query + ").", e); } } @@ -813,12 +671,12 @@ public final class MediaController { public void prepareFromUri(Uri uri, Bundle extras) { if (uri == null || Uri.EMPTY.equals(uri)) { throw new IllegalArgumentException( - "You must specify a non-empty Uri for prepareFromUri"); + "You must specify a non-empty Uri for prepareFromUri."); } try { mSessionBinder.prepareFromUri(uri, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + uri + ")", e); + Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); } } @@ -829,7 +687,7 @@ public final class MediaController { try { mSessionBinder.play(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play", e); + Log.wtf(TAG, "Error calling play.", e); } } @@ -843,12 +701,12 @@ public final class MediaController { public void playFromMediaId(String mediaId, Bundle extras) { if (TextUtils.isEmpty(mediaId)) { throw new IllegalArgumentException( - "You must specify a non-empty String for playFromMediaId"); + "You must specify a non-empty String for playFromMediaId."); } try { mSessionBinder.playFromMediaId(mediaId, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + mediaId + ")", e); + Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); } } @@ -870,7 +728,7 @@ public final class MediaController { try { mSessionBinder.playFromSearch(query, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + query + ")", e); + Log.wtf(TAG, "Error calling play(" + query + ").", e); } } @@ -884,12 +742,12 @@ public final class MediaController { public void playFromUri(Uri uri, Bundle extras) { if (uri == null || Uri.EMPTY.equals(uri)) { throw new IllegalArgumentException( - "You must specify a non-empty Uri for playFromUri"); + "You must specify a non-empty Uri for playFromUri."); } try { mSessionBinder.playFromUri(uri, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + uri + ")", e); + Log.wtf(TAG, "Error calling play(" + uri + ").", e); } } @@ -901,7 +759,7 @@ public final class MediaController { try { mSessionBinder.skipToQueueItem(id); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling skipToItem(" + id + ")", e); + Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); } } @@ -913,7 +771,7 @@ public final class MediaController { try { mSessionBinder.pause(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling pause", e); + Log.wtf(TAG, "Error calling pause.", e); } } @@ -925,7 +783,7 @@ public final class MediaController { try { mSessionBinder.stop(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling stop", e); + Log.wtf(TAG, "Error calling stop.", e); } } @@ -938,7 +796,7 @@ public final class MediaController { try { mSessionBinder.seekTo(pos); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling seekTo", e); + Log.wtf(TAG, "Error calling seekTo.", e); } } @@ -950,7 +808,7 @@ public final class MediaController { try { mSessionBinder.fastForward(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling fastForward", e); + Log.wtf(TAG, "Error calling fastForward.", e); } } @@ -961,7 +819,7 @@ public final class MediaController { try { mSessionBinder.next(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling next", e); + Log.wtf(TAG, "Error calling next.", e); } } @@ -973,7 +831,7 @@ public final class MediaController { try { mSessionBinder.rewind(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rewind", e); + Log.wtf(TAG, "Error calling rewind.", e); } } @@ -984,7 +842,7 @@ public final class MediaController { try { mSessionBinder.previous(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling previous", e); + Log.wtf(TAG, "Error calling previous.", e); } } @@ -999,36 +857,7 @@ public final class MediaController { try { mSessionBinder.rate(rating); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rate", e); - } - } - - /** - * Set the repeat mode for this session. - * - * @param repeatMode The repeat mode. Must be one of the followings: - * {@link PlaybackState#REPEAT_MODE_NONE}, - * {@link PlaybackState#REPEAT_MODE_ONE}, - * {@link PlaybackState#REPEAT_MODE_ALL} - */ - public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) { - try { - mSessionBinder.repeatMode(repeatMode); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setRepeatMode", e); - } - } - - /** - * Set the shuffle mode for this session. - * - * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable. - */ - public void setShuffleModeEnabled(boolean enabled) { - try { - mSessionBinder.shuffleMode(enabled); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling shuffleMode", e); + Log.wtf(TAG, "Error calling rate.", e); } } @@ -1042,7 +871,7 @@ public final class MediaController { public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, @Nullable Bundle args) { if (customAction == null) { - throw new IllegalArgumentException("CustomAction cannot be null"); + throw new IllegalArgumentException("CustomAction cannot be null."); } sendCustomAction(customAction.getAction(), args); } @@ -1058,12 +887,12 @@ public final class MediaController { */ public void sendCustomAction(@NonNull String action, @Nullable Bundle args) { if (TextUtils.isEmpty(action)) { - throw new IllegalArgumentException("CustomAction cannot be null"); + throw new IllegalArgumentException("CustomAction cannot be null."); } try { mSessionBinder.sendCustomAction(action, args); } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCustomAction", e); + Log.d(TAG, "Dead object in sendCustomAction.", e); } } } @@ -1233,21 +1062,6 @@ public final class MediaController { } } - @Override - public void onRepeatModeChanged(int repeatMode) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_REPEAT_MODE, repeatMode, null); - } - } - - @Override - public void onShuffleModeChanged(boolean enabled) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_SHUFFLE_MODE, enabled, null); - } - } } private final static class MessageHandler extends Handler { @@ -1286,12 +1100,6 @@ public final class MediaController { case MSG_UPDATE_VOLUME: mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj); break; - case MSG_UPDATE_REPEAT_MODE: - mCallback.onRepeatModeChanged((int) msg.obj); - break; - case MSG_UPDATE_SHUFFLE_MODE: - mCallback.onShuffleModeChanged((boolean) msg.obj); - break; case MSG_DESTROYED: mCallback.onSessionDestroyed(); break; diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index f10f4427d6f0..dfd2bb35d6ea 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -93,12 +93,6 @@ public final class MediaSession { public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; /** - * Set this flag on the session to indicate that it handles queue - * management commands through its {@link Callback}. - */ - public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2; - - /** * System only flag for a session that needs to have priority over all other * sessions. This flag ensures this session will receive media button events * regardless of the current ordering in the system. @@ -112,7 +106,6 @@ public final class MediaSession { @IntDef(flag = true, value = { FLAG_HANDLES_MEDIA_BUTTONS, FLAG_HANDLES_TRANSPORT_CONTROLS, - FLAG_HANDLES_QUEUE_COMMANDS, FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) public @interface SessionFlags { } @@ -493,41 +486,6 @@ public final class MediaSession { } /** - * Set the repeat mode for this session. - * <p> - * Note that if this method is not called before, {@link MediaController#getRepeatMode} - * will return {@link PlaybackState#REPEAT_MODE_NONE}. - * - * @param repeatMode The repeat mode. Must be one of the followings: - * {@link PlaybackState#REPEAT_MODE_NONE}, - * {@link PlaybackState#REPEAT_MODE_ONE}, - * {@link PlaybackState#REPEAT_MODE_ALL} - */ - public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) { - try { - mBinder.setRepeatMode(repeatMode); - } catch (RemoteException e) { - Log.e(TAG, "Error in setRepeatMode.", e); - } - } - - /** - * Set the shuffle mode for this session. - * <p> - * Note that if this method is not called before, {@link MediaController#isShuffleModeEnabled} - * will return {@code false}. - * - * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable. - */ - public void setShuffleModeEnabled(boolean enabled) { - try { - mBinder.setShuffleModeEnabled(enabled); - } catch (RemoteException e) { - Log.e(TAG, "Error in setShuffleModeEnabled.", e); - } - } - - /** * Set some extras that can be associated with the {@link MediaSession}. No assumptions should * be made as to how a {@link MediaController} will handle these extras. * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts. @@ -646,34 +604,10 @@ public final class MediaSession { postToCallback(CallbackMessageHandler.MSG_RATE, rating); } - private void dispatchRepeatMode(int repeatMode) { - postToCallback(CallbackMessageHandler.MSG_REPEAT_MODE, repeatMode); - } - - private void dispatchShuffleMode(boolean enabled) { - postToCallback(CallbackMessageHandler.MSG_SHUFFLE_MODE, enabled); - } - private void dispatchCustomAction(String action, Bundle args) { postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); } - private void dispatchAddQueueItem(MediaDescription description) { - postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM, description); - } - - private void dispatchAddQueueItem(MediaDescription description, int index) { - postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index); - } - - private void dispatchRemoveQueueItem(MediaDescription description) { - postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM, description); - } - - private void dispatchRemoveQueueItemAt(int index) { - postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index); - } - private void dispatchMediaButton(Intent mediaButtonIntent) { postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent); } @@ -695,22 +629,10 @@ public final class MediaSession { postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd); } - private void postToCallback(int what, int arg1) { - postToCallback(what, null, arg1); - } - private void postToCallback(int what, Object obj) { postToCallback(what, obj, null); } - private void postToCallback(int what, Object obj, int arg1) { - synchronized (mLock) { - if (mCallback != null) { - mCallback.post(what, obj, arg1); - } - } - } - private void postToCallback(int what, Object obj, Bundle extras) { synchronized (mLock) { if (mCallback != null) { @@ -1048,33 +970,6 @@ public final class MediaSession { } /** - * Override to handle the setting of the repeat mode. - * <p> - * You should call {@link #setRepeatMode} before end of this method in order to notify - * the change to the {@link MediaController}, or {@link MediaController#getRepeatMode} - * could return an invalid value. - * - * @param repeatMode The repeat mode which is one of followings: - * {@link PlaybackState#REPEAT_MODE_NONE}, - * {@link PlaybackState#REPEAT_MODE_ONE}, - * {@link PlaybackState#REPEAT_MODE_ALL} - */ - public void onSetRepeatMode(@PlaybackState.RepeatMode int repeatMode) { - } - - /** - * Override to handle the setting of the shuffle mode. - * <p> - * You should call {@link #setShuffleModeEnabled} before the end of this method in order to - * notify the change to the {@link MediaController}, or - * {@link MediaController#isShuffleModeEnabled} could return an invalid value. - * - * @param enabled true when the shuffle mode is enabled, false otherwise. - */ - public void onSetShuffleModeEnabled(boolean enabled) { - } - - /** * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be * performed. * @@ -1084,47 +979,6 @@ public final class MediaSession { */ public void onCustomAction(@NonNull String action, @Nullable Bundle extras) { } - - /** - * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given - * {@link MediaDescription description} at the end of the play queue. - * - * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be - * inserted. - */ - public void onAddQueueItem(MediaDescription description) { - } - - /** - * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given - * {@link MediaDescription description} at the specified position in the play queue. - * - * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be - * inserted. - * @param index The index at which the created {@link QueueItem} is to be inserted. - */ - public void onAddQueueItem(MediaDescription description, int index) { - } - - /** - * Called when a {@link MediaController} wants to remove the first occurrence of the - * specified {@link QueueItem} with the given {@link MediaDescription description} - * in the play queue. - * - * @param description The {@link MediaDescription} for denoting the {@link QueueItem} to be - * removed. - */ - public void onRemoveQueueItem(MediaDescription description) { - } - - /** - * Called when a {@link MediaController} wants to remove a {@link QueueItem} at the - * specified position in the play queue. - * - * @param index The index of the element to be removed. - */ - public void onRemoveQueueItemAt(int index) { - } } /** @@ -1297,22 +1151,6 @@ public final class MediaSession { } @Override - public void onRepeatMode(int repeatMode) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchRepeatMode(repeatMode); - } - } - - @Override - public void onShuffleMode(boolean enabled) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchShuffleMode(enabled); - } - } - - @Override public void onCustomAction(String action, Bundle args) { MediaSession session = mMediaSession.get(); if (session != null) { @@ -1321,38 +1159,6 @@ public final class MediaSession { } @Override - public void onAddQueueItem(MediaDescription description) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchAddQueueItem(description); - } - } - - @Override - public void onAddQueueItemAt(MediaDescription description, int index) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchAddQueueItem(description, index); - } - } - - @Override - public void onRemoveQueueItem(MediaDescription description) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchRemoveQueueItem(description); - } - } - - @Override - public void onRemoveQueueItemAt(int index) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchRemoveQueueItemAt(index); - } - } - - @Override public void onAdjustVolume(int direction) { MediaSession session = mMediaSession.get(); if (session != null) { @@ -1376,7 +1182,7 @@ public final class MediaSession { */ public static final class QueueItem implements Parcelable { /** - * This id is reserved. No items can be explicitly asigned this id. + * This id is reserved. No items can be explicitly assigned this id. */ public static final int UNKNOWN_ID = -1; @@ -1485,15 +1291,9 @@ public final class MediaSession { private static final int MSG_REWIND = 17; private static final int MSG_SEEK_TO = 18; private static final int MSG_RATE = 19; - private static final int MSG_REPEAT_MODE = 20; - private static final int MSG_SHUFFLE_MODE = 21; - private static final int MSG_CUSTOM_ACTION = 22; - private static final int MSG_ADJUST_VOLUME = 23; - private static final int MSG_SET_VOLUME = 24; - private static final int MSG_ADD_QUEUE_ITEM = 25; - private static final int MSG_ADD_QUEUE_ITEM_AT = 26; - private static final int MSG_REMOVE_QUEUE_ITEM = 27; - private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28; + private static final int MSG_CUSTOM_ACTION = 20; + private static final int MSG_ADJUST_VOLUME = 21; + private static final int MSG_SET_VOLUME = 22; private MediaSession.Callback mCallback; @@ -1582,33 +1382,15 @@ public final class MediaSession { case MSG_RATE: mCallback.onSetRating((Rating) msg.obj); break; - case MSG_REPEAT_MODE: - mCallback.onSetRepeatMode(msg.arg1); - break; - case MSG_SHUFFLE_MODE: - mCallback.onSetShuffleModeEnabled((boolean) msg.obj); - break; case MSG_CUSTOM_ACTION: mCallback.onCustomAction((String) msg.obj, msg.getData()); break; - case MSG_ADD_QUEUE_ITEM: - mCallback.onAddQueueItem((MediaDescription) msg.obj); - break; - case MSG_ADD_QUEUE_ITEM_AT: - mCallback.onAddQueueItem((MediaDescription) msg.obj, msg.arg1); - break; - case MSG_REMOVE_QUEUE_ITEM: - mCallback.onRemoveQueueItem((MediaDescription) msg.obj); - break; - case MSG_REMOVE_QUEUE_ITEM_AT: - mCallback.onRemoveQueueItemAt(msg.arg1); - break; case MSG_ADJUST_VOLUME: synchronized (mLock) { vp = mVolumeProvider; } if (vp != null) { - vp.onAdjustVolume(msg.arg1); + vp.onAdjustVolume((int) msg.obj); } break; case MSG_SET_VOLUME: @@ -1616,7 +1398,7 @@ public final class MediaSession { vp = mVolumeProvider; } if (vp != null) { - vp.onSetVolumeTo(msg.arg1); + vp.onSetVolumeTo((int) msg.obj); } break; } diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index 1ea61093d869..8283c8b967e8 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -45,8 +45,7 @@ public final class PlaybackState implements Parcelable { ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, - ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI, - ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE_ENABLED}) + ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI}) @Retention(RetentionPolicy.SOURCE) public @interface Actions {} @@ -177,20 +176,6 @@ public final class PlaybackState implements Parcelable { public static final long ACTION_PREPARE_FROM_URI = 1 << 17; /** - * Indicates this session supports the set repeat mode command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SET_REPEAT_MODE = 1 << 18; - - /** - * Indicates this session supports the set shuffle mode enabled command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 1 << 19; - - /** * @hide */ @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING, @@ -296,30 +281,6 @@ public final class PlaybackState implements Parcelable { */ public final static long PLAYBACK_POSITION_UNKNOWN = -1; - /** - * @hide - */ - @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL}) - @Retention(RetentionPolicy.SOURCE) - public @interface RepeatMode {} - /** - * Use this value with {@link MediaController.TransportControls#setRepeatMode} - * to indicate that the playback will be stopped at the end of the playing media list. - */ - public final static int REPEAT_MODE_NONE = 0; - - /** - * Use this value with {@link MediaController.TransportControls#setRepeatMode} - * to indicate that the playback of the current playing media item will be repeated. - */ - public final static int REPEAT_MODE_ONE = 1; - - /** - * Use this value with {@link MediaController.TransportControls#setRepeatMode} - * to indicate that the playback of the playing media list will be repeated. - */ - public final static int REPEAT_MODE_ALL = 2; - private final int mState; private final long mPosition; private final long mBufferedPosition; @@ -466,8 +427,6 @@ public final class PlaybackState implements Parcelable { * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li> * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li> * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li> - * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li> - * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li> * </ul> */ @Actions @@ -1002,8 +961,6 @@ public final class PlaybackState implements Parcelable { * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li> * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li> * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li> - * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li> - * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li> * </ul> * * @param actions The set of actions allowed. diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 1b55380ed5a9..71f9ba257aa3 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -436,6 +436,14 @@ public final class TvContract { public static final String PARAM_CANONICAL_GENRE = "canonical_genre"; /** + * A query, update or delete URI parameter that allows the caller to operate only on preview or + * non-preview channels. If set to "true", the operation affects the rows for preview channels + * only. If set to "false", the operation affects the rows for non-preview channels only. + * @hide + */ + public static final String PARAM_PREVIEW = "preview"; + + /** * Builds an ID that uniquely identifies a TV input service. * * @param name The {@link ComponentName} of the TV input service to build ID for. diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl index e95154ff4540..84f41f6c3afe 100644 --- a/media/java/android/service/media/IMediaBrowserService.aidl +++ b/media/java/android/service/media/IMediaBrowserService.aidl @@ -19,10 +19,8 @@ oneway interface IMediaBrowserService { void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks); void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks); - void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks); - void search(String query, in Bundle extras, in ResultReceiver cb, - IMediaBrowserServiceCallbacks callbacks); + void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks); void addSubscription(String uri, in IBinder token, in Bundle options, IMediaBrowserServiceCallbacks callbacks); void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks); diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index d372efbea5ca..b52906ddd1ae 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -89,15 +89,8 @@ public abstract class MediaBrowserService extends Service { */ public static final String KEY_MEDIA_ITEM = "media_item"; - /** - * A key for passing the list of MediaItems to the ResultReceiver in search. - * @hide - */ - public static final String KEY_SEARCH_RESULTS = "search_results"; - private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0; private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1; - private static final int RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED = 1 << 2; private static final int RESULT_ERROR = -1; private static final int RESULT_OK = 0; @@ -105,7 +98,7 @@ public abstract class MediaBrowserService extends Service { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED, - RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED }) + RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED }) private @interface ResultFlags { } private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); @@ -137,7 +130,6 @@ public abstract class MediaBrowserService extends Service { * * @see #onLoadChildren * @see #onLoadItem - * @see #onSearch */ public class Result<T> { private Object mDebug; @@ -330,23 +322,6 @@ public abstract class MediaBrowserService extends Service { } }); } - - @Override - public void search(final String query, Bundle extras, ResultReceiver receiver, - final IMediaBrowserServiceCallbacks callbacks) { - mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = mConnections.get(b); - if (connection == null) { - Log.w(TAG, "search for callback that isn't registered query=" + query); - return; - } - performSearch(query, extras, connection, receiver); - } - }); - } } @Override @@ -472,32 +447,6 @@ public abstract class MediaBrowserService extends Service { } /** - * Called to get the search result. - * <p> - * Implementations must call {@link Result#sendResult result.sendResult}. If - * the search will be an expensive operation {@link Result#detach result.detach} - * may be called before returning from this function, and then {@link Result#sendResult - * result.sendResult} called when the search has been completed. - * </p><p> - * In case there are no search results, call {@link Result#sendResult} with an empty list. - * In case there are some errors happened, call {@link Result#sendResult result.sendResult} - * with {@code null}, which will invoke {@link MediaBrowser.SearchCallback#onError}. - * </p><p> - * The default implementation will invoke {@link MediaBrowser.SearchCallback#onError}. - * </p> - * - * @param query The search query sent from the media browser. It contains keywords separated - * by space. - * @param extras The bundle of service-specific arguments sent from the media browser. - * @param result The {@link Result} to send the search result. - */ - public void onSearch(@NonNull String query, Bundle extras, - Result<List<MediaBrowser.MediaItem>> result) { - result.setFlags(RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED); - result.sendResult(null); - } - - /** * Call to set the media session. * <p> * This should be called as soon as possible during the service's startup. @@ -545,16 +494,16 @@ public abstract class MediaBrowserService extends Service { * media browser service when connecting and retrieving the root id for browsing, or null if * none. The contents of this bundle may affect the information returned when browsing. * - * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren}, - * {@link #onLoadItem} or {@link #onSearch}. + * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} or + * {@link #onLoadItem}. * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED */ public final Bundle getBrowserRootHints() { if (mCurConnection == null) { - throw new IllegalStateException("This should be called inside of onLoadChildren," - + " onLoadItem or onSearch methods"); + throw new IllegalStateException("This should be called inside of onLoadChildren or" + + " onLoadItem methods"); } return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints); } @@ -771,34 +720,6 @@ public abstract class MediaBrowserService extends Service { } } - private void performSearch(String query, Bundle extras, final ConnectionRecord connection, - final ResultReceiver receiver) { - final Result<List<MediaBrowser.MediaItem>> result = - new Result<List<MediaBrowser.MediaItem>>(query) { - @Override - void onResultSent(List<MediaBrowser.MediaItem> items, @ResultFlags int flag) { - if ((flag & RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED) != 0 - || items == null) { - receiver.send(RESULT_ERROR, null); - return; - } - Bundle bundle = new Bundle(); - bundle.putParcelableArray(KEY_SEARCH_RESULTS, - items.toArray(new MediaBrowser.MediaItem[0])); - receiver.send(RESULT_OK, bundle); - } - }; - - mCurConnection = connection; - onSearch(query, extras, result); - mCurConnection = null; - - if (!result.isDone()) { - throw new IllegalStateException("onSearch must call detach() or sendResult()" - + " before returning for query=" + query); - } - } - /** * Contains information that the browser service needs to send to the client * when first connected. diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index ae16949791bc..8e5821024cee 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "sensor" #include <utils/Log.h> -#include <android/hardware_buffer.h> #include <android/looper.h> #include <android/sensor.h> #include <android/sharedmem.h> @@ -28,6 +27,7 @@ #include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/Timers.h> +#include <vndk/hardware_buffer.h> #include <poll.h> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index 246bd2bc7ce0..5beb41253d48 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -314,8 +314,8 @@ public class DeviceDiscoveryService extends Service { } class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> { - //TODO wifi icon private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); + private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3); private Drawable icon(int drawableRes) { Drawable icon = getResources().getDrawable(drawableRes, null); @@ -345,6 +345,11 @@ public class DeviceDiscoveryService extends Service { device.equals(mSelectedDevice) ? Color.GRAY : Color.TRANSPARENT); + textView.setCompoundDrawablesWithIntrinsicBounds( + device.device instanceof android.net.wifi.ScanResult + ? WIFI_ICON + : BLUETOOTH_ICON, + null, null, null); textView.setOnClickListener((view) -> { mSelectedDevice = device; notifyDataSetChanged(); @@ -357,8 +362,6 @@ public class DeviceDiscoveryService extends Service { textView.setTextColor(Color.BLACK); final int padding = DeviceChooserActivity.getPadding(getResources()); textView.setPadding(padding, padding, padding, padding); - textView.setCompoundDrawablesWithIntrinsicBounds( - BLUETOOTH_ICON, null, null, null); textView.setCompoundDrawablePadding(padding); return textView; } diff --git a/packages/SettingsLib/res/layout/usage_view.xml b/packages/SettingsLib/res/layout/usage_view.xml index 1d56668a82d0..151d1ee3b899 100644 --- a/packages/SettingsLib/res/layout/usage_view.xml +++ b/packages/SettingsLib/res/layout/usage_view.xml @@ -76,17 +76,23 @@ android:id="@+id/bottom_label_space" android:layout_width="@dimen/usage_graph_labels_width" android:layout_height="wrap_content"/> - <include android:id="@+id/label_start" - layout="@layout/usage_side_label" /> - - <Space + <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_weight="1" /> + android:layout_weight="1" + android:orientation="horizontal" + android:layoutDirection="ltr"> + <include android:id="@+id/label_start" + layout="@layout/usage_side_label" /> - <include android:id="@+id/label_end" - layout="@layout/usage_side_label" /> + <Space + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" /> + <include android:id="@+id/label_end" + layout="@layout/usage_side_label" /> + </LinearLayout> </LinearLayout> </LinearLayout> diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java new file mode 100644 index 000000000000..972ea347e6dd --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.accessibility; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; + +import java.util.List; + +/** + * A helper class to assist determining the state of the accessibility button that can appear + * within the software-rendered navigation area. + */ +public class AccessibilityButtonHelper { + public static boolean isRequestedByMagnification(Context ctx) { + return Settings.Secure.getInt(ctx.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1; + } + + public static boolean isRequestedByAccessibilityService(Context ctx) { + final AccessibilityManager accessibilityManager = ctx.getSystemService( + AccessibilityManager.class); + List<AccessibilityServiceInfo> services = + accessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + if (services != null) { + for (int i = 0, size = services.size(); i < size; i++) { + if ((services.get(i).flags + & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) + != 0) { + return true; + } + } + } + return false; + } + + public static boolean isRequested(Context ctx) { + return isRequestedByMagnification(ctx) || isRequestedByAccessibilityService(ctx); + } + + public static boolean isDeviceSupported(Resources res) { + return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 8a86c13abb7d..c10970467383 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -29,6 +29,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageStats; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; @@ -45,6 +46,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.text.format.Formatter; +import android.util.IconDrawableFactory; import android.util.Log; import android.util.SparseArray; @@ -52,6 +54,7 @@ import com.android.internal.util.ArrayUtils; import com.android.settingslib.R; import java.io.File; +import java.io.IOException; import java.text.Collator; import java.text.Normalizer; import java.text.Normalizer.Form; @@ -61,6 +64,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Objects; +import java.util.UUID; import java.util.regex.Pattern; /** @@ -92,6 +96,7 @@ public class ApplicationsState { final Context mContext; final PackageManager mPm; + final IconDrawableFactory mDrawableFactory; final IPackageManager mIpm; final UserManager mUm; final StorageStatsManager mStats; @@ -114,7 +119,7 @@ public class ApplicationsState { final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>(); List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>(); long mCurId = 1; - String mCurComputingSizeUuid; + UUID mCurComputingSizeUuid; String mCurComputingSizePkg; int mCurComputingSizeUserId; boolean mSessionsChanged; @@ -129,6 +134,7 @@ public class ApplicationsState { private ApplicationsState(Application app) { mContext = app; mPm = mContext.getPackageManager(); + mDrawableFactory = IconDrawableFactory.newInstance(mContext); mIpm = AppGlobals.getPackageManager(); mUm = mContext.getSystemService(UserManager.class); mStats = mContext.getSystemService(StorageStatsManager.class); @@ -324,7 +330,7 @@ public class ApplicationsState { return; } synchronized (entry) { - entry.ensureIconLocked(mContext, mPm); + entry.ensureIconLocked(mContext, mDrawableFactory); } } @@ -334,15 +340,23 @@ public class ApplicationsState { AppEntry entry = mEntriesMap.get(userId).get(packageName); if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { mBackgroundHandler.post(() -> { - final StorageStats stats = mStats.queryStatsForPackage(entry.info.volumeUuid, - packageName, UserHandle.of(userId)); - final PackageStats legacyStats = new PackageStats(packageName, userId); - legacyStats.codeSize = stats.getCodeBytes(); - legacyStats.dataSize = stats.getDataBytes(); - legacyStats.cacheSize = stats.getCacheBytes(); try { - mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacyStats, true); - } catch (RemoteException ignored) { + final StorageStats stats = mStats.queryStatsForPackage( + entry.info.storageUuid, packageName, UserHandle.of(userId)); + final PackageStats legacy = new PackageStats(packageName, userId); + legacy.codeSize = stats.getCodeBytes(); + legacy.dataSize = stats.getDataBytes(); + legacy.cacheSize = stats.getCacheBytes(); + try { + mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true); + } catch (RemoteException ignored) { + } + } catch (NameNotFoundException | IOException e) { + Log.w(TAG, "Failed to query stats: " + e); + try { + mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false); + } catch (RemoteException ignored) { + } } }); } @@ -932,7 +946,7 @@ public class ApplicationsState { AppEntry entry = mAppEntries.get(i); if (entry.icon == null || !entry.mounted) { synchronized (entry) { - if (entry.ensureIconLocked(mContext, mPm)) { + if (entry.ensureIconLocked(mContext, mDrawableFactory)) { if (!mRunning) { mRunning = true; Message m = mMainHandler.obtainMessage( @@ -979,7 +993,7 @@ public class ApplicationsState { mMainHandler.sendMessage(m); } entry.sizeLoadStart = now; - mCurComputingSizeUuid = entry.info.volumeUuid; + mCurComputingSizeUuid = entry.info.storageUuid; mCurComputingSizePkg = entry.info.packageName; mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid); @@ -988,17 +1002,17 @@ public class ApplicationsState { final StorageStats stats = mStats.queryStatsForPackage( mCurComputingSizeUuid, mCurComputingSizePkg, UserHandle.of(mCurComputingSizeUserId)); - final PackageStats legacyStats = new PackageStats( + final PackageStats legacy = new PackageStats( mCurComputingSizePkg, mCurComputingSizeUserId); - legacyStats.codeSize = stats.getCodeBytes(); - legacyStats.dataSize = stats.getDataBytes(); - legacyStats.cacheSize = stats.getCacheBytes(); + legacy.codeSize = stats.getCodeBytes(); + legacy.dataSize = stats.getDataBytes(); + legacy.cacheSize = stats.getCacheBytes(); try { - mStatsObserver.onGetStatsCompleted(legacyStats, true); + mStatsObserver.onGetStatsCompleted(legacy, true); } catch (RemoteException ignored) { } - } catch (IllegalStateException e) { - Log.e(TAG,"An exception occurred while fetching app size", e); + } catch (NameNotFoundException | IOException e) { + Log.w(TAG, "Failed to query stats: " + e); try { mStatsObserver.onGetStatsCompleted(null, false); } catch (RemoteException ignored) { @@ -1255,10 +1269,10 @@ public class ApplicationsState { } } - boolean ensureIconLocked(Context context, PackageManager pm) { + boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) { if (this.icon == null) { if (this.apkFile.exists()) { - this.icon = getBadgedIcon(pm); + this.icon = drawableFactory.getBadgedIcon(info); return true; } else { this.mounted = false; @@ -1270,19 +1284,13 @@ public class ApplicationsState { // its icon. if (this.apkFile.exists()) { this.mounted = true; - this.icon = getBadgedIcon(pm); + this.icon = drawableFactory.getBadgedIcon(info); return true; } } return false; } - private Drawable getBadgedIcon(PackageManager pm) { - // Do badging ourself so that it comes from the user of the app not the current user. - return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info), - new UserHandle(UserHandle.getUserId(info.uid))); - } - public String getVersion(Context context) { try { return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName; diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java index 21835738fd69..34fdc9ddd99d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java @@ -19,9 +19,12 @@ package com.android.settingslib.applications; import android.app.usage.StorageStats; import android.app.usage.StorageStatsManager; import android.content.Context; +import android.content.pm.PackageManager; import android.os.UserHandle; import android.support.annotation.VisibleForTesting; +import java.io.IOException; + /** * StorageStatsSource wraps the StorageStatsManager for testability purposes. */ @@ -32,17 +35,21 @@ public class StorageStatsSource { mStorageStatsManager = context.getSystemService(StorageStatsManager.class); } - public StorageStatsSource.ExternalStorageStats getExternalStorageStats(String volumeUuid, UserHandle user) { + public StorageStatsSource.ExternalStorageStats getExternalStorageStats(String volumeUuid, + UserHandle user) throws IOException { return new StorageStatsSource.ExternalStorageStats( mStorageStatsManager.queryExternalStatsForUser(volumeUuid, user)); } - public StorageStatsSource.AppStorageStats getStatsForUid(String volumeUuid, int uid) { - return new StorageStatsSource.AppStorageStatsImpl(mStorageStatsManager.queryStatsForUid(volumeUuid, uid)); + public StorageStatsSource.AppStorageStats getStatsForUid(String volumeUuid, int uid) + throws IOException { + return new StorageStatsSource.AppStorageStatsImpl( + mStorageStatsManager.queryStatsForUid(volumeUuid, uid)); } public StorageStatsSource.AppStorageStats getStatsForPackage( - String volumeUuid, String packageName, UserHandle user) { + String volumeUuid, String packageName, UserHandle user) + throws PackageManager.NameNotFoundException, IOException { return new StorageStatsSource.AppStorageStatsImpl( mStorageStatsManager.queryStatsForPackage(volumeUuid, packageName, user)); } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java index 88f133ce57c2..ccf7a0bfdfc6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java @@ -20,11 +20,16 @@ import android.app.AppGlobals; import android.app.usage.StorageStatsManager; import android.content.Context; import android.os.storage.VolumeInfo; +import android.util.Log; + +import java.io.IOException; /** * PrivateStorageInfo provides information about the total and free storage on the device. */ public class PrivateStorageInfo { + private static final String TAG = "PrivateStorageInfo"; + public final long freeBytes; public final long totalBytes; @@ -41,8 +46,12 @@ public class PrivateStorageInfo { long privateTotalBytes = 0; for (VolumeInfo info : sm.getVolumes()) { if (info.getType() == VolumeInfo.TYPE_PRIVATE && info.isMountedReadable()) { - privateTotalBytes += sm.getTotalBytes(stats, info); - privateFreeBytes += sm.getFreeBytes(stats, info); + try { + privateTotalBytes += sm.getTotalBytes(stats, info); + privateFreeBytes += sm.getFreeBytes(stats, info); + } catch (IOException e) { + Log.w(TAG, e); + } } } return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes); @@ -51,6 +60,11 @@ public class PrivateStorageInfo { public static long getTotalSize(VolumeInfo info, long totalInternalStorage) { final Context context = AppGlobals.getInitialApplication(); final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class); - return stats.getTotalBytes(info.getFsUuid()); + try { + return stats.getTotalBytes(info.getFsUuid()); + } catch (IOException e) { + Log.w(TAG, e); + return 0; + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java index 11060e6c1a05..b57b6cc544fa 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java @@ -20,6 +20,7 @@ import android.app.usage.StorageStatsManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import java.io.IOException; import java.util.List; /** @@ -49,12 +50,12 @@ public class StorageManagerVolumeProvider implements StorageVolumeProvider { } @Override - public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) { + public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException { return stats.getTotalBytes(volume.getFsUuid()); } @Override - public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) { + public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException { return stats.getFreeBytes(volume.getFsUuid()); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java index 60e10a125d0b..ea28fe68e91b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java @@ -32,6 +32,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseLongArray; +import java.io.IOException; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.List; @@ -154,7 +155,7 @@ public class StorageMeasurement { try { details.totalSize = mStats.getTotalBytes(mVolume.fsUuid); details.availSize = mStats.getFreeBytes(mVolume.fsUuid); - } catch (IllegalStateException e) { + } catch (IOException e) { // The storage volume became null while we were measuring it. Log.w(TAG, e); return details; @@ -169,8 +170,14 @@ public class StorageMeasurement { final HashMap<String, Long> mediaMap = new HashMap<>(); details.mediaSize.put(user.id, mediaMap); - final ExternalStorageStats stats = mStats - .queryExternalStatsForUser(mSharedVolume.fsUuid, UserHandle.of(user.id)); + final ExternalStorageStats stats; + try { + stats = mStats.queryExternalStatsForUser(mSharedVolume.fsUuid, + UserHandle.of(user.id)); + } catch (IOException e) { + Log.w(TAG, e); + continue; + } addValue(details.usersSize, user.id, stats.getTotalBytes()); @@ -190,8 +197,13 @@ public class StorageMeasurement { if ((mVolume.getType() == VolumeInfo.TYPE_PRIVATE) && mVolume.isMountedReadable()) { for (UserInfo user : users) { - final StorageStats stats = mStats.queryStatsForUser(mVolume.fsUuid, - UserHandle.of(user.id)); + final StorageStats stats; + try { + stats = mStats.queryStatsForUser(mVolume.fsUuid, UserHandle.of(user.id)); + } catch (IOException e) { + Log.w(TAG, e); + continue; + } // Only count code once against current user if (user.id == UserHandle.myUserId()) { diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java index e5d85d147bee..4c4541315e07 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java @@ -19,6 +19,7 @@ package com.android.settingslib.deviceinfo; import android.app.usage.StorageStatsManager; import android.os.storage.VolumeInfo; +import java.io.IOException; import java.util.List; /** @@ -46,12 +47,12 @@ public interface StorageVolumeProvider { * * @pre The volume is a private volume and is readable. */ - long getTotalBytes(StorageStatsManager stats, VolumeInfo volume); + long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException; /** * Returns the free bytes for a given storage volume. * * @pre The volume is a private volume and is readable. */ - long getFreeBytes(StorageStatsManager stats, VolumeInfo volume); + long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException; } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java index d6bde8132327..9d09737a14dc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java @@ -40,6 +40,8 @@ public final class CategoryKey { "com.android.settings.category.ia.language"; public static final String CATEGORY_SYSTEM_DEVELOPMENT = "com.android.settings.category.ia.development"; + public static final String CATEGORY_NOTIFICATIONS = + "com.android.settings.category.ia.notifications"; public static final Map<String, String> KEY_COMPAT_MAP; diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java index 6bcf256a8033..fb48054b7bca 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java @@ -513,7 +513,7 @@ public class TileUtils { return null; } if (!providerMap.containsKey(authority)) { - providerMap.put(authority, context.getContentResolver().acquireProvider(uri)); + providerMap.put(authority, context.getContentResolver().acquireUnstableProvider(uri)); } return providerMap.get(authority); } diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java index 231fc695d344..3f826cc00b8b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java @@ -27,6 +27,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.util.IconDrawableFactory; import android.util.Log; import java.util.ArrayList; @@ -48,10 +49,12 @@ public class RecentLocationApps { private final PackageManager mPackageManager; private final Context mContext; + private final IconDrawableFactory mDrawableFactory; public RecentLocationApps(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mDrawableFactory = IconDrawableFactory.newInstance(context); } /** @@ -146,8 +149,7 @@ public class RecentLocationApps { } final UserHandle userHandle = new UserHandle(userId); - Drawable appIcon = mPackageManager.getApplicationIcon(appInfo); - Drawable icon = mPackageManager.getUserBadgedIcon(appIcon, userHandle); + Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId); CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo); CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle); if (appLabel.toString().contentEquals(badgedAppLabel)) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 5310b7a604b0..8415dc5cbc4a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -302,8 +302,10 @@ public class TileUtilsTest { when(mIContentProvider.call(anyString(), eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY), any())).thenReturn(bundle); - when(mContentResolver.acquireProvider(anyString())).thenReturn(mIContentProvider); - when(mContentResolver.acquireProvider(any(Uri.class))).thenReturn(mIContentProvider); + when(mContentResolver.acquireUnstableProvider(anyString())) + .thenReturn(mIContentProvider); + when(mContentResolver.acquireUnstableProvider(any(Uri.class))) + .thenReturn(mIContentProvider); TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, null /* defaultCategory */, outTiles, false /* usePriority */, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 169a034f447c..48429e8b2b05 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1109,6 +1109,33 @@ public class SettingsProvider extends ContentProvider { // Retrieve the ssaid from the table if present. final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name); + // If the app is an Instant App use its stored SSAID instead of our own. + final String instantSsaid; + final long token = Binder.clearCallingIdentity(); + try { + instantSsaid = mPackageManager.getInstantAppAndroidId(callingPkg.packageName, + owningUserId); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Failed to get Instant App Android ID", e); + return null; + } finally { + Binder.restoreCallingIdentity(token); + } + if (instantSsaid != null) { + // Use the stored value if it is still valid. + if (ssaid != null && instantSsaid.equals(ssaid.getValue())) { + return ssaid; + } + // The value has changed, update the stored value. + final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked( + SETTINGS_TYPE_SSAID, owningUserId); + final boolean success = ssaidSettings.insertSettingLocked(name, instantSsaid, null, + true, callingPkg.packageName); + if (!success) { + throw new IllegalStateException("Failed to update instant app android id"); + } + return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name); + } // Lazy initialize ssaid if not yet present in ssaid table. if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) { diff --git a/packages/SystemUI/res/color/segmented_button_text_selector.xml b/packages/SystemUI/res/color/segmented_button_text_selector.xml index 537cbb84f647..d4f01816a608 100644 --- a/packages/SystemUI/res/color/segmented_button_text_selector.xml +++ b/packages/SystemUI/res/color/segmented_button_text_selector.xml @@ -18,4 +18,4 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" android:color="?android:attr/textColorPrimary"/> <item android:alpha="0.58" android:color="?android:attr/colorForeground"/> -</selector>
\ No newline at end of file +</selector> diff --git a/packages/SystemUI/res/drawable/ic_dnd.xml b/packages/SystemUI/res/drawable/ic_dnd.xml index 17ecf217b86b..e658e687821b 100644 --- a/packages/SystemUI/res/drawable/ic_dnd.xml +++ b/packages/SystemUI/res/drawable/ic_dnd.xml @@ -17,7 +17,8 @@ android:height="24dp" android:viewportHeight="48.0" android:viewportWidth="48.0" - android:width="24dp" > + android:width="24dp" + android:tint="?android:attr/colorControlNormal"> <path android:fillColor="#FFFFFFFF" diff --git a/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml index 487597454e27..0515b35fb875 100644 --- a/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml +++ b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml @@ -17,7 +17,8 @@ android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" - android:width="24dp" > + android:width="24dp" + android:tint="?android:attr/colorControlNormal"> <path android:fillColor="#FFFFFFFF" diff --git a/packages/SystemUI/res/drawable/ic_info_outline.xml b/packages/SystemUI/res/drawable/ic_info_outline.xml new file mode 100644 index 000000000000..a4a3e9aab239 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_info_outline.xml @@ -0,0 +1,25 @@ +<!-- + 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="32dp" + android:height="32dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml index 5aecebae1865..169cc71722a2 100644 --- a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml +++ b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml @@ -16,7 +16,6 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="root" - android:alpha="0.3" android:height="48dp" android:width="48dp" android:viewportHeight="48" diff --git a/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml b/packages/SystemUI/res/drawable/ic_pause_white.xml index 5b65f100490c..5b65f100490c 100644 --- a/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml +++ b/packages/SystemUI/res/drawable/ic_pause_white.xml diff --git a/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml b/packages/SystemUI/res/drawable/ic_play_arrow_white.xml index ddc9e8dd17a0..ddc9e8dd17a0 100644 --- a/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml +++ b/packages/SystemUI/res/drawable/ic_play_arrow_white.xml diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml index 236fdac09c4b..5e7cd974818a 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml +++ b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml @@ -17,7 +17,8 @@ Copyright (C) 2014 The Android Open Source Project android:width="6.0dp" android:height="32dp" android:viewportWidth="6.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?android:attr/textColorSecondary"> <path android:fillColor="#FFFFFFFF" android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/> diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml index c510972a5df8..0dca1db32f88 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml +++ b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml @@ -17,7 +17,8 @@ Copyright (C) 2014 The Android Open Source Project android:width="6.0dp" android:height="32dp" android:viewportWidth="6.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?android:attr/textColorSecondary"> <path android:fillColor="#FFFFFFFF" android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/> diff --git a/packages/SystemUI/res/drawable/ic_skip_next_white.xml b/packages/SystemUI/res/drawable/ic_skip_next_white.xml new file mode 100644 index 000000000000..040c7e642241 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_skip_next_white.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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="#FFFFFF" + android:pathData="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" /> + <path + android:pathData="M0 0h24v24H0z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_skip_previous_white.xml b/packages/SystemUI/res/drawable/ic_skip_previous_white.xml new file mode 100644 index 000000000000..b9b94b73a00f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_skip_previous_white.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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="#FFFFFF" + android:pathData="M6 6h2v12H6zm3.5 6l8.5 6V6z" /> + <path + android:pathData="M0 0h24v24H0z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_volume_accessibility.xml b/packages/SystemUI/res/drawable/ic_volume_accessibility.xml index 657efaa213d4..88fb2d2f782c 100644 --- a/packages/SystemUI/res/drawable/ic_volume_accessibility.xml +++ b/packages/SystemUI/res/drawable/ic_volume_accessibility.xml @@ -18,8 +18,9 @@ android:width="32dp" android:height="32dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> <path android:fillColor="#FFFFFFFF" android:pathData="M20.5,6c-2.61,0.7 -5.67,1 -8.5,1s-5.89,-0.3 -8.5,-1L3,8c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2V9c2,-0.17 4.14,-0.5 6,-1l-0.5,-2zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/> -</vector>
\ No newline at end of file +</vector> diff --git a/packages/SystemUI/res/drawable/segmented_buttons_background.xml b/packages/SystemUI/res/drawable/segmented_buttons_background.xml index b243dc7ce8c0..755c917cf20c 100644 --- a/packages/SystemUI/res/drawable/segmented_buttons_background.xml +++ b/packages/SystemUI/res/drawable/segmented_buttons_background.xml @@ -17,6 +17,6 @@ <corners android:radius="@dimen/borderless_button_radius" /> - <solid android:color="@color/segmented_buttons_background" /> + <solid android:color="?android:attr/colorPrimaryDark" /> -</shape>
\ No newline at end of file +</shape> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml index d7463a446657..be9a7e266c6f 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="8.5dp" - android:height="17dp" - android:viewportWidth="12.0" - android:viewportHeight="24.0"> + android:width="17dp" + android:height="17dp" + android:viewportWidth="12.0" + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M3.500000,11.000000L1.800000,11.000000L1.800000,4.400000L0.200000,5.100000L0.200000,3.700000l3.100000,-1.300000l0.200000,0.000000L3.500000,11.000000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml index 6309b6d9903b..fd7a65841129 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="9.208dp" + android:width="17dp" android:height="17dp" android:viewportWidth="13.0" - android:viewportHeight="24.0"> + android:viewportHeight="13.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M2.000000,6.000000l0.800000,0.000000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000s0.200000,-0.500000 0.200000,-0.900000c0.000000,-0.300000 -0.100000,-0.600000 -0.200000,-0.800000S3.200000,3.700000 2.900000,3.700000C2.700000,3.700000 2.500000,3.800000 2.300000,4.000000S2.100000,4.400000 2.100000,4.700000L0.500000,4.700000C0.500000,4.000000 0.700000,3.400000 1.100000,3.000000s1.000000,-0.600000 1.700000,-0.600000c0.800000,0.000000 1.400000,0.200000 1.900000,0.600000s0.700000,1.000000 0.700000,1.800000c0.000000,0.400000 -0.100000,0.700000 -0.300000,1.100000S4.600000,6.500000 4.300000,6.600000C4.700000,6.800000 5.000000,7.100000 5.200000,7.400000s0.300000,0.700000 0.300000,1.200000c0.000000,0.800000 -0.200000,1.400000 -0.700000,1.800000s-1.100000,0.700000 -1.900000,0.700000c-0.700000,0.000000 -1.300000,-0.200000 -1.800000,-0.600000s-0.700000,-1.000000 -0.700000,-1.800000L2.000000,8.700000C2.000000,9.000000 2.100000,9.300000 2.300000,9.500000s0.400000,0.300000 0.600000,0.300000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000S3.900000,9.000000 3.900000,8.600000c0.000000,-0.500000 -0.100000,-0.800000 -0.300000,-1.000000S3.200000,7.300000 2.800000,7.300000L2.000000,7.300000L2.000000,6.000000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml index 4067ae5b4800..02c4ab6161c6 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="8.5dp" + android:width="17dp" android:height="17dp" android:viewportWidth="12.0" - android:viewportHeight="24.0"> + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M4.600000,7.800000l0.700000,0.000000l0.000000,1.300000L4.600000,9.100000L4.600000,11.000000L3.000000,11.000000L3.000000,9.200000L0.100000,9.200000L0.000000,8.100000L3.000000,2.500000l1.700000,0.000000L4.700000,7.800000zM1.600000,7.800000L3.000000,7.800000l0.000000,-3.000000L2.900000,5.000000L1.600000,7.800000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml index 3cdd3e104bfb..daf40618c24a 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml @@ -14,10 +14,10 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="17.0dp" + android:width="25.5dp" android:height="17.0dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportWidth="18.0" + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml index acaa9b1c2be8..cd0cc6585391 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="3.541dp" + android:width="7.083dp" android:height="17dp" android:viewportWidth="5.0" - android:viewportHeight="24.0"> + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml index 7985237d8a44..92ed49c71189 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="4.958dp" + android:width="9.154dp" android:height="17dp" android:viewportWidth="7.0" - android:viewportHeight="24.0"> + android:viewportHeight="13"> <path android:fillColor="#FFFFFFFF" android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml index fda87612582e..ca61b6f0299c 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="4.25dp" + android:width="9.5dp" android:height="17dp" android:viewportWidth="6.0" - android:viewportHeight="24.0"> + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml index c08ff20c54fc..add96b4352e8 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="9.208dp" + android:width="18.417dp" android:height="17dp" - android:viewportWidth="13.0" - android:viewportHeight="24.0"> + android:viewportWidth="13" + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M2.000000,9.700000l2.000000,0.000000L4.000000,11.000000L0.300000,11.000000L0.300000,2.500000L2.000000,2.500000L2.000000,9.700000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml index db18fad637e7..8811d2f9b7ba 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml @@ -14,10 +14,10 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="17.0dp" + android:width="25.0dp" android:height="17.0dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportWidth="18.0" + android:viewportHeight="12.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml index 4baa472acb88..363e231cf03c 100644 --- a/packages/SystemUI/res/drawable/stat_sys_roaming.xml +++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml @@ -14,10 +14,10 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="8.5dp" + android:width="4.25dp" android:height="17dp" android:viewportWidth="6.0" - android:viewportHeight="12.0"> + android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/> diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_disconnected.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_disconnected.xml new file mode 100644 index 000000000000..f4e8af468b76 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_disconnected.xml @@ -0,0 +1,42 @@ +<!-- + 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="18.41dp" + android:height="17dp" + android:viewportWidth="26.0" + android:viewportHeight="24.0"> + <path + android:fillColor="?attr/backgroundColor" + android:pathData="M21.0,8.5 + c0.85,0.0 1.6,0.23 2.3,0.62l2.24,-2.79 + C25.1,5.96 20.26,2.0 13.0,2.0 + S0.9,5.9 0.42,6.32 + l12.57,15.6 4.21,-5.17 + c-0.76,-0.87 -1.22,-2.0 -1.22,-3.25 + c0.0,-2.76 2.24,-5.0 5.0,-5.0z" + android:fillAlpha=".3"/> + <path + android:fillColor="?attr/backgroundColor" + android:pathData="M21.0,10.0 + c-1.93,0.0 -3.5,1.57 -3.5,3.5l1.75,0.0 + c0.0,-0.9 0.78,-1.75 1.75,-1.75s1.7,0.78 1.75,1.75 + c0.0,0.48 -0.2,0.92 -0.51,1.24l-1.09,1.1 + c-0.6,0.63 -1.02,1.51 -1.02,2.47l0.0,0.44l1.75,0.0 + c0.0,-1.3 0.39,-1.84 1.03,-2.47l0.78,-0.8 + c0.5,-0.5 0.82,-1.2 0.82,-1.97 + C24.5,11.57 22.93,10.0 21.0,10.0z + m-0.95,11.95l1.9,0.0l0.0,-1.9l-1.9,0.0l0.0,1.9z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml index 5169de46ea29..c1856fa30e7e 100644 --- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml +++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml @@ -20,5 +20,8 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="?attr/backgroundColor" - android:pathData="M13.000000,2.000000C7.700000,2.000000 3.700000,3.900000 0.400000,6.400000L13.000000,22.000000L25.600000,6.500000C22.299999,4.000000 18.299999,2.000000 13.000000,2.000000zM13.000000,18.600000L3.300000,7.000000l0.000000,0.000000l0.000000,0.000000C6.000000,5.300000 8.700000,4.000000 13.000000,4.000000s7.000000,1.400000 9.700000,3.000000l0.000000,0.000000l0.000000,0.000000L13.000000,18.600000z"/> + android:pathData="M17.500000,16.500000L5.800000,3.400000c0.000000,0.000000 0.000000,0.000000 0.000000,0.000000l-2.700000,-3.000000L1.600000,1.800000l2.200000,2.500000c-2.000000,1.000000 -3.200000,2.000000 -3.400000,2.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l3.200000,-3.900000l2.400000,2.700000l1.500000,-1.400000L17.500000,16.500000L17.500000,16.500000z"/> + <path + android:fillColor="?attr/backgroundColor" + android:pathData="M25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000c-1.900000,0.000000 -3.600000,0.300000 -5.200000,0.700000L18.700001,15.000000L25.600000,6.500000z"/> </vector> diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml index 33effba53059..6d4365c644e4 100644 --- a/packages/SystemUI/res/layout/mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/mobile_signal_group.xml @@ -63,20 +63,21 @@ systemui:hasOverlappingRendering="false" /> <ImageView - android:id="@+id/mobile_type" + android:id="@+id/mobile_roaming" android:layout_height="wrap_content" android:layout_width="wrap_content" + android:src="@drawable/stat_sys_roaming" + android:contentDescription="@string/accessibility_data_connection_roaming" + android:visibility="gone" /> <ImageView - android:id="@+id/mobile_roaming" + android:id="@+id/mobile_type" android:layout_width="wrap_content" android:layout_height="17dp" - android:paddingStart="22dp" + android:paddingStart="19dp" android:paddingTop="1.5dp" android:paddingBottom="3dp" android:scaleType="fitCenter" - android:src="@drawable/stat_sys_roaming" - android:contentDescription="@string/accessibility_data_connection_roaming" android:visibility="gone" /> </FrameLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index ff22ffb319fb..0c9858dc8397 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -114,6 +114,7 @@ android:layout_height="wrap_content" android:layout_gravity="end|center_vertical" android:layout_weight="1" + android:contentDescription="@string/notification_channel_switch_accessibility" android:background="@null" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml index 5bd64defd2d1..b70f24b4d552 100644 --- a/packages/SystemUI/res/layout/notification_snooze.xml +++ b/packages/SystemUI/res/layout/notification_snooze.xml @@ -19,47 +19,56 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="@dimen/snooze_snackbar_min_height" - android:id="@+id/notification_snooze" - android:clickable="true" - android:gravity="center_vertical" - android:orientation="horizontal" - android:paddingStart="24dp" - android:paddingEnd="24dp" - android:background="@color/snooze_snackbar_bg"> - - <TextView - android:id="@+id/snooze_option_default" - style="@style/TextAppearance.SnoozeSnackBar" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical" - android:drawableTint="@android:color/white" - android:drawableEnd="@drawable/notification_expand_more"/> - - <android.widget.Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" - /> - - <TextView - android:id="@+id/undo" - style="@style/TextAppearance.SnoozeSnackBar.Button" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginEnd="8dp" - android:layout_marginStart="8dp" - android:background="@drawable/btn_borderless_rect" - android:layout_gravity="end" - android:text="@string/snooze_undo" /> - + android:orientation="vertical" + android:background="@color/notification_guts_bg_color" + android:theme="@*android:style/Theme.DeviceDefault.Light"> + + <RelativeLayout + android:layout_width="match_parent" + android:id="@+id/notification_snooze" + android:layout_height="@dimen/snooze_snackbar_min_height"> + + <TextView + android:id="@+id/snooze_option_default" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:paddingStart="16dp" + android:textColor="#DD000000" + android:paddingEnd="4dp"/> + + <ImageView + android:id="@+id/expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toEndOf="@+id/snooze_option_default" + android:layout_centerVertical="true" + android:paddingTop="1dp" + android:tint="#9E9E9E" /> + + <TextView + android:id="@+id/undo" + style="@style/TextAppearance.NotificationInfo.Button" + android:layout_width="wrap_content" + android:layout_height="36dp" + android:layout_marginEnd="8dp" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + android:text="@string/snooze_undo" /> + </RelativeLayout> + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="0.5dp" + android:background="#9E9E9E" /> + <LinearLayout android:id="@+id/snooze_options" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" android:paddingBottom="8dp" - android:orientation="vertical"/> - + android:orientation="vertical" /> + </com.android.systemui.statusbar.NotificationSnooze> diff --git a/packages/SystemUI/res/layout/notification_snooze_option.xml b/packages/SystemUI/res/layout/notification_snooze_option.xml new file mode 100644 index 000000000000..aaf45f34585b --- /dev/null +++ b/packages/SystemUI/res/layout/notification_snooze_option.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2017, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_marginStart="@*android:dimen/notification_content_margin_start" + android:layout_marginEnd="@*android:dimen/notification_content_margin_end" + android:gravity="center_vertical" + android:textSize="14sp" + android:textColor="#DD000000"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml index 8667a5a8f5cd..bb1b288cf3bf 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer.xml @@ -14,41 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:paddingBottom="@dimen/qs_tile_padding_top" - android:paddingTop="@dimen/qs_tile_padding_top" > + android:paddingTop="@dimen/qs_tile_padding_top" + android:paddingStart="@dimen/qs_footer_padding_start" + android:paddingEnd="@dimen/qs_footer_padding_end" + android:gravity="center_vertical" + android:background="?android:attr/colorPrimaryDark" > <TextView android:id="@+id/footer_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerHorizontal="true" - android:textSize="@dimen/qs_tile_text_size" /> + android:gravity="start" + android:layout_weight="1" + android:textAppearance="@style/TextAppearance.QS.TileLabel" + android:textColor="?android:attr/textColorSecondary"/> <ImageView android:id="@+id/footer_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginEnd="8dp" - android:layout_toStartOf="@id/footer_text" - android:contentDescription="@null" - android:src="@drawable/ic_qs_vpn" - android:visibility="invisible" /> - - <!-- Only shown if both images are visible --> - <ImageView - android:id="@+id/footer_icon2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginEnd="8dp" - android:layout_toStartOf="@id/footer_icon" + android:layout_width="@dimen/qs_footer_icon_size" + android:layout_height="@dimen/qs_footer_icon_size" android:contentDescription="@null" - android:src="@drawable/ic_qs_network_logging" - android:visibility="invisible" /> + android:src="@drawable/ic_info_outline" /> -</RelativeLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml index da7e4d79fc46..5766dc1c5b4f 100644 --- a/packages/SystemUI/res/layout/signal_cluster_view.xml +++ b/packages/SystemUI/res/layout/signal_cluster_view.xml @@ -52,9 +52,54 @@ android:alpha="0.0" /> </FrameLayout> + <ViewStub + android:id="@+id/connected_device_signals_stub" + android:layout="@layout/connected_device_signal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + <LinearLayout + android:id="@+id/mobile_signal_group" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + > + </LinearLayout> + <View + android:id="@+id/wifi_signal_spacer" + android:layout_width="@dimen/status_bar_wifi_signal_spacer_width" + android:layout_height="4dp" + android:visibility="gone" + /> + <FrameLayout + android:id="@+id/no_sims_combo" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:contentDescription="@string/accessibility_no_sims"> + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:theme="@style/DualToneLightTheme" + android:id="@+id/no_sims" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/stat_sys_no_sims" + /> + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:theme="@style/DualToneDarkTheme" + android:id="@+id/no_sims_dark" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/stat_sys_no_sims" + android:alpha="0.0" + /> + </FrameLayout> + <View + android:id="@+id/wifi_airplane_spacer" + android:layout_width="@dimen/status_bar_airplane_spacer_width" + android:layout_height="4dp" + android:visibility="gone" + /> <FrameLayout android:layout_height="17dp" - android:layout_width="wrap_content"> + android:layout_width="wrap_content" + android:paddingStart="2dp"> <ImageView android:id="@+id/wifi_in" android:layout_height="wrap_content" @@ -96,50 +141,6 @@ android:layout_width="wrap_content" /> </FrameLayout> - <View - android:id="@+id/wifi_signal_spacer" - android:layout_width="@dimen/status_bar_wifi_signal_spacer_width" - android:layout_height="4dp" - android:visibility="gone" - /> - <ViewStub - android:id="@+id/connected_device_signals_stub" - android:layout="@layout/connected_device_signal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <LinearLayout - android:id="@+id/mobile_signal_group" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - > - </LinearLayout> - <FrameLayout - android:id="@+id/no_sims_combo" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:contentDescription="@string/accessibility_no_sims"> - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:theme="@style/DualToneLightTheme" - android:id="@+id/no_sims" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/stat_sys_no_sims" - /> - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:theme="@style/DualToneDarkTheme" - android:id="@+id/no_sims_dark" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/stat_sys_no_sims" - android:alpha="0.0" - /> - </FrameLayout> - <View - android:id="@+id/wifi_airplane_spacer" - android:layout_width="@dimen/status_bar_airplane_spacer_width" - android:layout_height="4dp" - android:visibility="gone" - /> <ImageView android:id="@+id/airplane" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml index c6bcd32901dd..61ac6f6991ff 100644 --- a/packages/SystemUI/res/layout/tv_pip_controls.xml +++ b/packages/SystemUI/res/layout/tv_pip_controls.xml @@ -40,7 +40,7 @@ android:layout_width="100dp" android:layout_height="wrap_content" android:layout_marginStart="-50dp" - android:src="@drawable/ic_pause_white_24dp" + android:src="@drawable/ic_pause_white" android:text="@string/pip_pause" android:visibility="gone" /> </merge> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index f461bb3ffedf..83a0b7bcdc65 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -96,10 +96,6 @@ <!-- The "inside" of a notification, reached via longpress --> <color name="notification_guts_bg_color">#eeeeee</color> - <!-- Colors of the snooze menu reached via snooze icon behind a notification --> - <color name="snooze_snackbar_bg">#FF4A4A4A</color> - <color name="snooze_snackbar_text">#FFA6BAFF</color> - <color name="assist_orb_color">#ffffff</color> <color name="keyguard_user_switcher_background_gradient_color">#77000000</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9c7a6a00a4fb..4edadea38a93 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -119,7 +119,7 @@ <dimen name="notification_menu_icon_padding">20dp</dimen> <!-- The minimum height for the snackbar shown after the snooze option has been chosen. --> - <dimen name="snooze_snackbar_min_height">48dp</dimen> + <dimen name="snooze_snackbar_min_height">56dp</dimen> <!-- The text size of options in the snooze menu. --> <dimen name="snooze_option_text_size">14sp</dimen> @@ -263,6 +263,9 @@ <dimen name="qs_detail_item_icon_size">24dp</dimen> <dimen name="qs_detail_item_icon_marginStart">0dp</dimen> <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen> + <dimen name="qs_footer_padding_start">16dp</dimen> + <dimen name="qs_footer_padding_end">24dp</dimen> + <dimen name="qs_footer_icon_size">16dp</dimen> <!-- Desired qs icon overlay size. --> <dimen name="qs_detail_icon_overlay_size">24dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index eeb28c825ffa..c9da889e281b 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -376,13 +376,13 @@ <string name="accessibility_no_sim">No SIM.</string> <!-- Content description of the cell data. [CHAR LIMIT=NONE] --> - <string name="accessibility_cell_data">Cellular Data</string> + <string name="accessibility_cell_data">Mobile Data</string> <!-- Content description of the cell data being enabled. [CHAR LIMIT=NONE] --> - <string name="accessibility_cell_data_on">Cellular Data On</string> + <string name="accessibility_cell_data_on">Mobile Data On</string> <!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] --> - <string name="accessibility_cell_data_off">Cellular Data Off</string> + <string name="accessibility_cell_data_off">Mobile Data Off</string> <!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_bluetooth_tether">Bluetooth tethering.</string> @@ -574,11 +574,11 @@ <!-- Title of dialog shown when 4G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] --> <string name="data_usage_disabled_dialog_4g_title">4G data is paused</string> <!-- Title of dialog shown when mobile data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] --> - <string name="data_usage_disabled_dialog_mobile_title">Cellular data is paused</string> + <string name="data_usage_disabled_dialog_mobile_title">Mobile data is paused</string> <!-- Title of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] --> <string name="data_usage_disabled_dialog_title">Data is paused</string> <!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] --> - <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using cellular data.\n\nIf you resume, charges may apply for data usage.</string> + <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage.</string> <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] --> <string name="data_usage_disabled_dialog_enable">Resume</string> @@ -749,7 +749,7 @@ <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] --> <string name="quick_settings_flashlight_label">Flashlight</string> <!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] --> - <string name="quick_settings_cellular_detail_title">Cellular data</string> + <string name="quick_settings_cellular_detail_title">Mobile data</string> <!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] --> <string name="quick_settings_cellular_detail_data_usage">Data usage</string> <!-- QuickSettings: Cellular detail panel, remaining data title [CHAR LIMIT=NONE] --> @@ -1466,6 +1466,15 @@ <item quantity="other"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g>, and <xliff:g id="number">%3$d</xliff:g> others</item> </plurals> + <!-- Notification: Control panel: Accessibility description for expanded inline controls view, used + to control settings about notifications related to the current notification. --> + <string name="notification_channel_controls_opened_accessibility">Notification controls for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> opened</string> + <!-- Notification: Control panel: Accessibility description for announcing the closing of the + inline controls view. --> + <string name="notification_channel_controls_closed_accessibility">Notification controls for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> closed</string> + <!-- Notification: Control panel: Accessibility description for switch that is used to enable + or disable notifications from this channel --> + <string name="notification_channel_switch_accessibility">Allow notifications from this channel</string> <!-- Notification: Control panel: Label for button that launches notification settings. Used when this app has defined more than a single channel for notifications. --> <string name="notification_all_categories">All Categories</string> @@ -1493,8 +1502,6 @@ <string name="snooze_option_30_min">30 minutes</string> <!-- Notification: Menu row: Snooze options: 1 hour option. [CHAR LIMIT=50]--> <string name="snooze_option_1_hour">1 hour</string> - <!-- Notification: Menu row: Snooze options: cancel snoozing option. [CHAR LIMIT=50] --> - <string name="snooze_option_dont_snooze">Cancel</string> <!-- Notification: Menu row: Snooze undo button label. [CHAR LIMIT=50]--> <string name="snooze_undo">UNDO</string> @@ -1888,6 +1895,18 @@ <!-- PiP minimize description. [CHAR LIMIT=NONE] --> <string name="pip_minimize_description" translatable="false">Drag or fling the PIP to the edges of the screen to minimize it.</string> + <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_play">Play</string> + + <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_pause">Pause</string> + + <!-- Button to skip to the next media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_skip_to_next">Skip to next</string> + + <!-- Button to skip to the prev media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_skip_to_prev">Skip to previous</string> + <!-- Tuner string --> <string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string> <!-- Tuner string --> @@ -1895,11 +1914,11 @@ <!-- Tuner string --> <string name="default_theme" translatable="false">Default</string> - <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=30] --> + <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] --> <string name="thermal_shutdown_title">Phone turned off due to heat</string> <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=100] --> <string name="thermal_shutdown_message">Your phone is now running normally</string> - <!-- Text body for dialog alerting user that their phone last shut down bewcause it got too hot. [CHAR LIMIT=300] --> + <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=450] --> <string name="thermal_shutdown_dialog_message">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t• Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t• Download or upload large files\n\t• Use your phone in high temperatures</string> <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] --> diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml index 41626fcb0a49..e578068163d7 100644 --- a/packages/SystemUI/res/values/strings_tv.xml +++ b/packages/SystemUI/res/values/strings_tv.xml @@ -23,8 +23,4 @@ <string name="pip_close">Close PIP</string> <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] --> <string name="pip_fullscreen">Full screen</string> - <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] --> - <string name="pip_play">Play</string> - <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] --> - <string name="pip_pause">Pause</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index c9479b8b41f7..dbdbd1e287aa 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -299,13 +299,13 @@ <style name="TextAppearance.Volume"> <item name="android:textStyle">normal</item> - <item name="android:textColor">#ffffffff</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:fontFamily">sans-serif</item> </style> <style name="TextAppearance.Volume.Header"> <item name="android:textSize">12sp</item> - <item name="android:textColor">@color/volume_slider_inactive</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> </style> <style name="TextAppearance.Volume.ZenSummary"> @@ -316,7 +316,7 @@ <style name="TextAppearance.Volume.ZenDetail"> <item name="android:textSize">14sp</item> <item name="android:fontFamily">sans-serif</item> - <item name="android:textColor">@*android:color/quaternary_device_default_settings</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> </style> <style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless"> @@ -386,20 +386,6 @@ <item name="android:paddingEnd">8dp</item> </style> - <style name="TextAppearance.SnoozeSnackBar"> - <item name="android:textSize">14sp</item> - <item name="android:fontFamily">sans-serif</item> - <item name="android:textColor">@android:color/white</item> - </style> - - <style name="TextAppearance.SnoozeSnackBar.Button"> - <item name="android:textSize">14sp</item> - <item name="android:textAllCaps">true</item> - <item name="android:fontFamily">sans-serif-medium</item> - <item name="android:gravity">center</item> - <item name="android:textColor">@color/snooze_snackbar_text</item> - </style> - <style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings"> <item name="android:colorBackground">?android:attr/colorSecondary</item> </style> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b447979be45d..616e5b9830ed 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -15,6 +15,7 @@ */ package com.android.keyguard; +import android.R.style; import android.app.Activity; import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; @@ -23,6 +24,7 @@ import android.os.UserHandle; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; @@ -73,7 +75,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + super(new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault), attrs, + defStyle); mSecurityModel = new KeyguardSecurityModel(context); mLockPatternUtils = new LockPatternUtils(context); mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java index 9b5577c850d2..b8cfa3e48c20 100644 --- a/packages/SystemUI/src/com/android/systemui/Interpolators.java +++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java @@ -41,6 +41,8 @@ public class Interpolators { public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f); public static final Interpolator HEADS_UP_APPEAR = new HeadsUpAppearInterpolator(); public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f); + public static final Interpolator PANEL_CLOSE_ACCELERATED + = new PathInterpolator(0.3f, 0, 0.5f, 1); /** * Interpolator to be used when animating a move based on a click. Pair with enough duration. diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 5a04108df807..8c4159abf061 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -462,11 +462,20 @@ public class SwipeHelper implements Gefingerpoken { int duration = SNAP_ANIM_LEN; anim.setDuration(duration); anim.addListener(new AnimatorListenerAdapter() { + boolean wasCancelled = false; + + @Override + public void onAnimationCancel(Animator animator) { + wasCancelled = true; + } + @Override public void onAnimationEnd(Animator animator) { mSnappingChild = false; - updateSwipeProgressFromOffset(animView, canBeDismissed); - mCallback.onChildSnappedBack(animView, targetLeft); + if (!wasCancelled) { + updateSwipeProgressFromOffset(animView, canBeDismissed); + mCallback.onChildSnappedBack(animView, targetLeft); + } } }); prepareSnapBackAnimation(animView, anim); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 86b36cafd17d..7139d59c6eef 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -149,6 +149,11 @@ public class DozeLog { log("screenOff why=" + why); } + public static void traceMissedTick(String delay) { + if (!ENABLED) return; + log("missedTick by=" + delay); + } + public static void traceKeyguard(boolean showing) { if (!ENABLED) return; log("keyguard " + showing); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 6098a20a0c64..03076cc4b7a8 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -20,6 +20,8 @@ import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.SystemClock; +import android.text.format.Formatter; +import android.util.Log; import com.android.systemui.util.wakelock.WakeLock; @@ -31,6 +33,7 @@ import java.util.GregorianCalendar; */ public class DozeUi implements DozeMachine.Part { + private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min private final Context mContext; private final AlarmManager mAlarmManager; private final DozeHost mHost; @@ -40,6 +43,7 @@ public class DozeUi implements DozeMachine.Part { private final AlarmManager.OnAlarmListener mTimeTick; private boolean mTimeTickScheduled = false; + private long mLastTimeTickElapsed = 0; public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, WakeLock wakeLock, DozeHost host, Handler handler) { @@ -103,15 +107,26 @@ public class DozeUi implements DozeMachine.Part { SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler); mTimeTickScheduled = true; + mLastTimeTickElapsed = SystemClock.elapsedRealtime(); } private void unscheduleTimeTick() { if (!mTimeTickScheduled) { return; } + verifyLastTimeTick(); mAlarmManager.cancel(mTimeTick); } + private void verifyLastTimeTick() { + long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed; + if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) { + String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick); + DozeLog.traceMissedTick(delay); + Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay); + } + } + private long roundToNextMinute(long timeInMillis) { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTimeInMillis(timeInMillis); @@ -127,6 +142,7 @@ public class DozeUi implements DozeMachine.Part { // Alarm was canceled, but we still got the callback. Ignore. return; } + verifyLastTimeTick(); mHost.dozeTimeTick(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java index 86bb0deff067..4b3cdfbcd71c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java @@ -39,7 +39,8 @@ public class PipDismissViewController { // This delay controls how long to wait before we show the target when the user first moves // the PIP, to prevent the target from animating if the user just wants to fling the PIP private static final int SHOW_TARGET_DELAY = 100; - private static final int SHOW_TARGET_DURATION = 200; + private static final int SHOW_TARGET_DURATION = 350; + private static final int HIDE_TARGET_DURATION = 225; private Context mContext; private WindowManager mWindowManager; @@ -96,7 +97,7 @@ public class PipDismissViewController { public void showDismissTarget() { mDismissView.animate() .alpha(1f) - .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) + .setInterpolator(Interpolators.LINEAR) .setStartDelay(SHOW_TARGET_DELAY) .setDuration(SHOW_TARGET_DURATION) .start(); @@ -109,9 +110,9 @@ public class PipDismissViewController { if (mDismissView != null) { mDismissView.animate() .alpha(0f) - .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) + .setInterpolator(Interpolators.LINEAR) .setStartDelay(0) - .setDuration(SHOW_TARGET_DURATION) + .setDuration(HIDE_TARGET_DURATION) .withEndAction(new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index c5653733bac7..28bd23ce9825 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -143,10 +143,10 @@ public class PipManager implements BasePipManager { @Override public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, - Rect animatingBounds, boolean fromImeAdjustement) { + Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) { mHandler.post(() -> { mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds, - fromImeAdjustement); + fromImeAdjustement, displayRotation); }); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java index 3a4caa9a1e88..62ec09be2f51 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java @@ -48,6 +48,8 @@ public class PipMediaController { private static final String ACTION_PLAY = "com.android.systemui.pip.phone.PLAY"; private static final String ACTION_PAUSE = "com.android.systemui.pip.phone.PAUSE"; + private static final String ACTION_NEXT = "com.android.systemui.pip.phone.NEXT"; + private static final String ACTION_PREV = "com.android.systemui.pip.phone.PREV"; /** * A listener interface to receive notification on changes to the media actions. @@ -67,6 +69,8 @@ public class PipMediaController { private RemoteAction mPauseAction; private RemoteAction mPlayAction; + private RemoteAction mNextAction; + private RemoteAction mPrevAction; private BroadcastReceiver mPlayPauseActionReceiver = new BroadcastReceiver() { @Override @@ -76,6 +80,10 @@ public class PipMediaController { mMediaController.getTransportControls().play(); } else if (action.equals(ACTION_PAUSE)) { mMediaController.getTransportControls().pause(); + } else if (action.equals(ACTION_NEXT)) { + mMediaController.getTransportControls().skipToNext(); + } else if (action.equals(ACTION_PREV)) { + mMediaController.getTransportControls().skipToPrevious(); } } }; @@ -95,6 +103,8 @@ public class PipMediaController { IntentFilter mediaControlFilter = new IntentFilter(); mediaControlFilter.addAction(ACTION_PLAY); mediaControlFilter.addAction(ACTION_PAUSE); + mediaControlFilter.addAction(ACTION_NEXT); + mediaControlFilter.addAction(ACTION_PREV); mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter); createMediaActions(); @@ -143,11 +153,21 @@ public class PipMediaController { int state = mMediaController.getPlaybackState().getState(); boolean isPlaying = MediaSession.isActiveState(state); long actions = mMediaController.getPlaybackState().getActions(); + + // Prev action + mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0); + mediaActions.add(mPrevAction); + + // Play/pause action if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) { mediaActions.add(mPlayAction); } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) { mediaActions.add(mPauseAction); } + + // Next action + mNextAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0); + mediaActions.add(mNextAction); return mediaActions; } @@ -157,15 +177,27 @@ public class PipMediaController { private void createMediaActions() { String pauseDescription = mContext.getString(R.string.pip_pause); mPauseAction = new RemoteAction(Icon.createWithResource(mContext, - R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription, + R.drawable.ic_pause_white), pauseDescription, pauseDescription, PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE), FLAG_UPDATE_CURRENT)); String playDescription = mContext.getString(R.string.pip_play); mPlayAction = new RemoteAction(Icon.createWithResource(mContext, - R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription, + R.drawable.ic_play_arrow_white), playDescription, playDescription, PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY), FLAG_UPDATE_CURRENT)); + + String nextDescription = mContext.getString(R.string.pip_skip_to_next); + mNextAction = new RemoteAction(Icon.createWithResource(mContext, + R.drawable.ic_skip_next_white), nextDescription, nextDescription, + PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NEXT), + FLAG_UPDATE_CURRENT)); + + String prevDescription = mContext.getString(R.string.pip_skip_to_prev); + mPrevAction = new RemoteAction(Icon.createWithResource(mContext, + R.drawable.ic_skip_previous_white), prevDescription, prevDescription, + PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PREV), + FLAG_UPDATE_CURRENT)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index ac0670348af2..79ac8160c095 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -221,6 +221,13 @@ public class PipMenuActivity extends Activity { } @Override + protected void onStop() { + super.onStop(); + + cancelDelayedFinish(); + } + + @Override protected void onDestroy() { super.onDestroy(); @@ -405,16 +412,26 @@ public class PipMenuActivity extends Activity { } else { actionsContainer.setVisibility(View.VISIBLE); if (mActionsGroup != null) { - mActionsGroup.removeAllViews(); + // Hide extra views + for (int i = mActions.size(); i < mActionsGroup.getChildCount(); i++) { + mActionsGroup.getChildAt(i).setVisibility(View.GONE); + } + // Add needed views + final LayoutInflater inflater = LayoutInflater.from(this); + while (mActionsGroup.getChildCount() < mActions.size()) { + final ImageView actionView = (ImageView) inflater.inflate( + R.layout.pip_menu_action, mActionsGroup, false); + mActionsGroup.addView(actionView); + } // Recreate the layout final boolean isLandscapePip = stackBounds != null && (stackBounds.width() > stackBounds.height()); - final LayoutInflater inflater = LayoutInflater.from(this); for (int i = 0; i < mActions.size(); i++) { final RemoteAction action = mActions.get(i); - final ImageView actionView = (ImageView) inflater.inflate( - R.layout.pip_menu_action, mActionsGroup, false); + final ImageView actionView = (ImageView) mActionsGroup.getChildAt(i); + + // TODO: Check if the action drawable has changed before we reload it action.getIcon().loadDrawableAsync(this, d -> { d.setTint(Color.WHITE); actionView.setImageDrawable(d); @@ -432,12 +449,11 @@ public class PipMenuActivity extends Activity { actionView.setAlpha(DISABLED_ACTION_ALPHA); actionView.setEnabled(false); } - if (isLandscapePip && i > 0) { - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) - actionView.getLayoutParams(); - lp.leftMargin = mBetweenActionPaddingLand; - } - mActionsGroup.addView(actionView); + + // Update the margin between actions + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) + actionView.getLayoutParams(); + lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 127296cd7f8e..67255d35590a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -316,7 +316,8 @@ public class PipMotionHelper { * Animates the PiP from the expanded state to the normal state after the menu is hidden. */ void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction, - Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized) { + Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized, + boolean immediate) { if (savedSnapFraction < 0f) { // If there are no saved snap fractions, then just use the current bounds savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), @@ -326,7 +327,11 @@ public class PipMotionHelper { if (minimized) { normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds); } - resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION); + if (immediate) { + movePip(normalBounds); + } else { + resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 3f26fddb1b3c..3223f9fd52b1 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -63,7 +63,7 @@ public class PipTouchHandler implements TunerService.Tunable { private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0; private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1; - private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200; + private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225; // Allow dragging the PIP to a location to close it private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true; @@ -78,6 +78,7 @@ public class PipTouchHandler implements TunerService.Tunable { private final PipDismissViewController mDismissViewController; private final PipSnapAlgorithm mSnapAlgorithm; private final AccessibilityManager mAccessibilityManager; + private boolean mShowPipMenuOnAnimationEnd = false; // The current movement bounds private Rect mMovementBounds = new Rect(); @@ -89,6 +90,11 @@ public class PipTouchHandler implements TunerService.Tunable { private Rect mExpandedMovementBounds = new Rect(); private int mExpandedShortestEdgeSize; + // Used to workaround an issue where the WM rotation happens before we are notified, allowing + // us to send stale bounds + private int mDeferResizeToNormalBoundsUntilRotation = -1; + private int mDisplayRotation; + private Handler mHandler = new Handler(); private Runnable mShowDismissAffordance = new Runnable() { @Override @@ -216,13 +222,18 @@ public class PipTouchHandler implements TunerService.Tunable { setMinimizedStateInternal(false); } mDismissViewController.destroyDismissTarget(); - mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(), - mMovementBounds, true /* allowMenuTimeout */); + mShowPipMenuOnAnimationEnd = true; } public void onPinnedStackAnimationEnded() { // Always synchronize the motion helper bounds once PiP animations finish mMotionHelper.synchronizePinnedStackBounds(); + + if (mShowPipMenuOnAnimationEnd) { + mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(), + mMovementBounds, true /* allowMenuTimeout */); + mShowPipMenuOnAnimationEnd = false; + } } @Override @@ -250,7 +261,7 @@ public class PipTouchHandler implements TunerService.Tunable { } public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds, - boolean fromImeAdjustement) { + boolean fromImeAdjustement, int displayRotation) { // Re-calculate the expanded bounds mNormalBounds = normalBounds; Rect normalMovementBounds = new Rect(); @@ -304,7 +315,17 @@ public class PipTouchHandler implements TunerService.Tunable { // above mNormalMovementBounds = normalMovementBounds; mExpandedMovementBounds = expandedMovementBounds; + mDisplayRotation = displayRotation; updateMovementBounds(mMenuState); + + // If we have a deferred resize, apply it now + if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) { + mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, + mNormalMovementBounds, mMovementBounds, mIsMinimized, + true /* immediate */); + mSavedSnapFraction = -1f; + mDeferResizeToNormalBoundsUntilRotation = -1; + } } private void onRegistrationChanged(boolean isRegistered) { @@ -474,11 +495,34 @@ public class PipTouchHandler implements TunerService.Tunable { // Try and restore the PiP to the closest edge, using the saved snap fraction // if possible if (resize) { - Rect normalBounds = new Rect(mNormalBounds); - mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized); + // This is a very special case: when the menu is expanded and visible, navigating to + // another activity can trigger auto-enter PiP, and if the revealed activity has a + // forced rotation set, then the controller will get updated with the new rotation + // of the display. However, at the same time, SystemUI will try to hide the menu by + // creating an animation to the normal bounds which are now stale. In such a case + // we defer the animation to the normal bounds until after the next + // onMovementBoundsChanged() call to get the bounds in the new orientation + if (mDeferResizeToNormalBoundsUntilRotation == -1) { + try { + int displayRotation = mPinnedStackController.getDisplayRotation(); + if (mDisplayRotation != displayRotation) { + mDeferResizeToNormalBoundsUntilRotation = displayRotation; + } + } catch (RemoteException e) { + Log.e(TAG, "Could not get display rotation from controller"); + } + } + + if (mDeferResizeToNormalBoundsUntilRotation == -1) { + Rect normalBounds = new Rect(mNormalBounds); + mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, + mNormalMovementBounds, mMovementBounds, mIsMinimized, + false /* immediate */); + mSavedSnapFraction = -1f; + } + } else { + mSavedSnapFraction = -1f; } - mSavedSnapFraction = -1f; } mMenuState = menuState; updateMovementBounds(menuState); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java index 4c81907c35f7..acea3b6b12ad 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java @@ -186,10 +186,10 @@ public class PipControlsView extends LinearLayout { } else { mPlayPauseButtonView.setVisibility(View.VISIBLE); if (state == PipManager.PLAYBACK_STATE_PLAYING) { - mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white_24dp); + mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white); mPlayPauseButtonView.setText(R.string.pip_pause); } else { - mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white_24dp); + mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white); mPlayPauseButtonView.setText(R.string.pip_play); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index f2f0d7a5a86c..657f08be8b52 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -178,7 +178,7 @@ public class PipManager implements BasePipManager { @Override public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, - Rect animatingBounds, boolean fromImeAdjustement) { + Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) { mHandler.post(() -> { mDefaultPipBounds.set(normalBounds); }); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index d51fe8a2f690..53c36e69ef7e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -167,6 +167,9 @@ public class QSFooter extends LinearLayout implements if (mAlarmShowing) { builder.addFloat(mDate, "alpha", 1, 0) .addFloat(mDateTimeGroup, "translationX", 0, -mDate.getWidth()); + } else { + mDate.setAlpha(1); + mDateTimeGroup.setTranslationX(0); } mAnimator = builder.build(); setExpansion(mExpansionAmount); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 38485c729b21..f5e096ebbbef 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -98,9 +98,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { setupTileLayout(); - mFooter = new QSSecurityFooter(this, context); - addView(mFooter.getView()); - mPageIndicator = LayoutInflater.from(context).inflate( R.layout.qs_page_indicator, this, false); addView(mPageIndicator); @@ -110,6 +107,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { addDivider(); + mFooter = new QSSecurityFooter(this, context); + addView(mFooter.getView()); + updateResources(); mBrightnessController = new BrightnessController(getContext(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 5b9d95d3e2c0..51c6ad88a2af 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -1,4 +1,3 @@ - /* * Copyright (C) 2014 The Android Open Source Project * @@ -51,24 +50,21 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic private final View mRootView; private final TextView mFooterText; private final ImageView mFooterIcon; - private final ImageView mFooterIcon2; private final Context mContext; private final Callback mCallback = new Callback(); private final SecurityController mSecurityController; private final ActivityStarter mActivityStarter; private final Handler mMainHandler; + private final View mDivider; private AlertDialog mDialog; private QSTileHost mHost; protected H mHandler; private boolean mIsVisible; - private boolean mIsIconVisible; - private boolean mIsIcon2Visible; private CharSequence mFooterTextContent = null; private int mFooterTextId; private int mFooterIconId; - private int mFooterIcon2Id; public QSSecurityFooter(QSPanel qsPanel, Context context) { mRootView = LayoutInflater.from(context) @@ -76,14 +72,13 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic mRootView.setOnClickListener(this); mFooterText = (TextView) mRootView.findViewById(R.id.footer_text); mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon); - mFooterIcon2 = (ImageView) mRootView.findViewById(R.id.footer_icon2); - mFooterIconId = R.drawable.ic_qs_vpn; - mFooterIcon2Id = R.drawable.ic_qs_network_logging; + mFooterIconId = R.drawable.ic_info_outline; mContext = context; mMainHandler = new Handler(Looper.getMainLooper()); mActivityStarter = Dependency.get(ActivityStarter.class); mSecurityController = Dependency.get(SecurityController.class); mHandler = new H(Dependency.get(Dependency.BG_LOOPER)); + mDivider = qsPanel == null ? null : qsPanel.getDivider(); } public void setHostEnvironment(QSTileHost host) { @@ -130,43 +125,100 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic } private void handleRefreshState() { - boolean isVpnEnabled = mSecurityController.isVpnEnabled(); - boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); - mIsIconVisible = isVpnEnabled || isNetworkLoggingEnabled; - mIsIcon2Visible = isVpnEnabled && isNetworkLoggingEnabled; - if (mSecurityController.isDeviceManaged()) { - final CharSequence organizationName = - mSecurityController.getDeviceOwnerOrganizationName(); - if (organizationName != null) { - mFooterTextContent = mContext.getResources().getString( - R.string.do_disclosure_with_name, organizationName); - } else { - mFooterTextContent = - mContext.getResources().getString(R.string.do_disclosure_generic); + final boolean isDeviceManaged = mSecurityController.isDeviceManaged(); + final boolean hasWorkProfile = mSecurityController.hasWorkProfile(); + final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser(); + final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile(); + final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); + final String vpnName = mSecurityController.getPrimaryVpnName(); + final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName(); + final CharSequence organizationName = mSecurityController.getDeviceOwnerOrganizationName(); + final CharSequence workProfileName = mSecurityController.getWorkProfileOrganizationName(); + // Update visibility of footer + mIsVisible = isDeviceManaged || hasCACerts || hasCACertsInWorkProfile || + vpnName != null || vpnNameWorkProfile != null; + // Update the string + mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile, + hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName, + vpnNameWorkProfile, organizationName, workProfileName); + // Update the icon + int footerIconId = vpnName != null || vpnNameWorkProfile != null + ? R.drawable.ic_qs_vpn + : R.drawable.ic_info_outline; + if (mFooterIconId != footerIconId) { + mFooterIconId = footerIconId; + mMainHandler.post(mUpdateIcon); + } + mMainHandler.post(mUpdateDisplayState); + } + + protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile, + boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled, + String vpnName, String vpnNameWorkProfile, CharSequence organizationName, + CharSequence workProfileName) { + if (isDeviceManaged) { + if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) { + if (organizationName == null) { + return mContext.getString( + R.string.quick_settings_disclosure_management_monitoring); + } + return mContext.getString( + R.string.quick_settings_disclosure_named_management_monitoring, + organizationName); } - mIsVisible = true; - int footerIconId = isVpnEnabled - ? R.drawable.ic_qs_vpn - : R.drawable.ic_qs_network_logging; - if (mFooterIconId != footerIconId) { - mFooterIconId = footerIconId; - mMainHandler.post(mUpdateIcon); + if (vpnName != null && vpnNameWorkProfile != null) { + if (organizationName == null) { + return mContext.getString(R.string.quick_settings_disclosure_management_vpns); + } + return mContext.getString(R.string.quick_settings_disclosure_named_management_vpns, + organizationName); } - } else { - boolean isBranded = mSecurityController.isVpnBranded(); - mFooterTextContent = mContext.getResources().getText( - isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer); - // Update the VPN footer icon, if needed. - int footerIconId = isVpnEnabled - ? (isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn) - : R.drawable.ic_qs_network_logging; - if (mFooterIconId != footerIconId) { - mFooterIconId = footerIconId; - mMainHandler.post(mUpdateIcon); + if (vpnName != null || vpnNameWorkProfile != null) { + if (organizationName == null) { + return mContext.getString( + R.string.quick_settings_disclosure_management_named_vpn, + vpnName != null ? vpnName : vpnNameWorkProfile); + } + return mContext.getString( + R.string.quick_settings_disclosure_named_management_named_vpn, + organizationName, + vpnName != null ? vpnName : vpnNameWorkProfile); } - mIsVisible = mIsIconVisible; + if (organizationName == null) { + return mContext.getString(R.string.quick_settings_disclosure_management); + } + return mContext.getString(R.string.quick_settings_disclosure_named_management, + organizationName); + } // end if(isDeviceManaged) + if (hasCACertsInWorkProfile) { + if (workProfileName == null) { + return mContext.getString( + R.string.quick_settings_disclosure_managed_profile_monitoring); + } + return mContext.getString( + R.string.quick_settings_disclosure_named_managed_profile_monitoring, + workProfileName); } - mMainHandler.post(mUpdateDisplayState); + if (hasCACerts) { + return mContext.getString(R.string.quick_settings_disclosure_monitoring); + } + if (vpnName != null && vpnNameWorkProfile != null) { + return mContext.getString(R.string.quick_settings_disclosure_vpns); + } + if (vpnNameWorkProfile != null) { + return mContext.getString(R.string.quick_settings_disclosure_managed_profile_named_vpn, + vpnNameWorkProfile); + } + if (vpnName != null) { + if (hasWorkProfile) { + return mContext.getString( + R.string.quick_settings_disclosure_personal_profile_named_vpn, + vpnName); + } + return mContext.getString(R.string.quick_settings_disclosure_named_vpn, + vpnName); + } + return null; } @Override @@ -182,18 +234,15 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic final String profileOwnerPackage = mSecurityController.getProfileOwnerName(); final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); final String primaryVpn = mSecurityController.getPrimaryVpnName(); - final String profileVpn = mSecurityController.getProfileVpnName(); + final String profileVpn = mSecurityController.getWorkProfileVpnName(); final CharSequence deviceOwnerOrganization = mSecurityController.getDeviceOwnerOrganizationName(); boolean hasProfileOwner = mSecurityController.hasProfileOwner(); - boolean isBranded = deviceOwnerPackage == null && mSecurityController.isVpnBranded(); mDialog = new SystemUIDialog(mContext); - if (!isBranded) { - mDialog.setTitle(getTitle(deviceOwnerPackage)); - } + mDialog.setTitle(getTitle(deviceOwnerPackage)); CharSequence msg = getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn, - profileVpn, deviceOwnerOrganization, hasProfileOwner, isBranded); + profileVpn, deviceOwnerOrganization, hasProfileOwner); if (deviceOwnerPackage == null) { mDialog.setMessage(msg); if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) { @@ -235,7 +284,7 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic } } - mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this); + mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); mDialog.show(); mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @@ -244,13 +293,13 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic return mContext.getString(R.string.status_bar_settings_settings_button); } - private String getPositiveButton(boolean isBranded) { - return mContext.getString(isBranded ? android.R.string.ok : R.string.quick_settings_done); + private String getPositiveButton() { + return mContext.getString(R.string.quick_settings_done); } protected CharSequence getMessage(String deviceOwnerPackage, String profileOwnerPackage, String primaryVpn, String profileVpn, CharSequence deviceOwnerOrganization, - boolean hasProfileOwner, boolean isBranded) { + boolean hasProfileOwner) { if (deviceOwnerPackage != null) { final SpannableStringBuilder message = new SpannableStringBuilder(); if (deviceOwnerOrganization != null) { @@ -273,13 +322,8 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic return mContext.getString(R.string.monitoring_description_app_personal_work, profileOwnerPackage, profileVpn, primaryVpn); } else { - if (isBranded) { - return mContext.getString(R.string.branded_monitoring_description_app_personal, - primaryVpn); - } else { - return mContext.getString(R.string.monitoring_description_app_personal, - primaryVpn); - } + return mContext.getString(R.string.monitoring_description_app_personal, + primaryVpn); } } else if (profileVpn != null) { return mContext.getString(R.string.monitoring_description_app_work, @@ -305,7 +349,6 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic @Override public void run() { mFooterIcon.setImageResource(mFooterIconId); - mFooterIcon2.setImageResource(mFooterIcon2Id); } }; @@ -316,8 +359,7 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic mFooterText.setText(mFooterTextContent); } mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE); - mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE); - mFooterIcon2.setVisibility(mIsIcon2Visible ? View.VISIBLE : View.INVISIBLE); + if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE); } }; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 7518527a3093..e457d7247434 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -77,6 +77,9 @@ public class QuickStatusBarHeader extends RelativeLayout { BatteryMeterView battery = findViewById(R.id.battery); battery.setForceShowPercent(true); + // Don't show the Wi-Fi indicator here, because it is shown just below in the tile. + SignalClusterView signalCluster = findViewById(R.id.signal_cluster); + signalCluster.setForceBlockWifi(); mActivityStarter = Dependency.get(ActivityStarter.class); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 6781c1673408..976efb2c289b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -340,12 +340,12 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { switch (state) { case Tile.STATE_UNAVAILABLE: return Utils.getDisabled(context, - Utils.getColorAttr(context, android.R.attr.textColorTertiary)); + Utils.getColorAttr(context, android.R.attr.textColorPrimary)); case Tile.STATE_INACTIVE: return Utils.getDisabled(context, - Utils.getColorAttr(context, android.R.attr.textColorSecondary)); + Utils.getColorAttr(context, android.R.attr.colorForeground)); case Tile.STATE_ACTIVE: - return Utils.getColorAttr(context, attr.textColorSecondary); + return Utils.getColorAttr(context, android.R.attr.textColorPrimary); default: Log.e("QSTile", "Invalid state " + state); return 0; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index d27bb858a16b..9b20a7a8879b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -259,22 +259,27 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { ArrayList<Item> items = new ArrayList<Item>(); final Collection<CachedBluetoothDevice> devices = mController.getDevices(); if (devices != null) { + int connectedDevices = 0; for (CachedBluetoothDevice device : devices) { if (device.getBondState() == BluetoothDevice.BOND_NONE) continue; final Item item = new Item(); item.icon = R.drawable.ic_qs_bluetooth_on; item.line1 = device.getName(); + item.tag = device; int state = device.getMaxConnectionState(); if (state == BluetoothProfile.STATE_CONNECTED) { item.icon = R.drawable.ic_qs_bluetooth_connected; item.line2 = mContext.getString(R.string.quick_settings_connected); item.canDisconnect = true; + items.add(connectedDevices, item); + connectedDevices++; } else if (state == BluetoothProfile.STATE_CONNECTING) { item.icon = R.drawable.ic_qs_bluetooth_connecting; item.line2 = mContext.getString(R.string.quick_settings_connecting); + items.add(connectedDevices, item); + } else { + items.add(item); } - item.tag = device; - items.add(item); } } mItems.setItems(items.toArray(new Item[items.size()])); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 92ff17a1f029..d74e3ac656f7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -128,7 +128,7 @@ public class CellularTile extends QSTileImpl<SignalState> { state.value = mDataController.isMobileDataSupported() && mDataController.isMobileDataEnabled(); state.icon = ResourceIcon.get(R.drawable.ic_data_unavailable); - state.state = cb.airplaneModeEnabled || !cb.enabled ? Tile.STATE_UNAVAILABLE + state.state = cb.airplaneModeEnabled || !cb.enabled || cb.noSim ? Tile.STATE_UNAVAILABLE : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; if (state.state == Tile.STATE_ACTIVE) { state.icon = ResourceIcon.get(R.drawable.ic_data_on); @@ -161,44 +161,27 @@ public class CellularTile extends QSTileImpl<SignalState> { private static final class CallbackInfo { boolean enabled; - boolean wifiEnabled; boolean airplaneModeEnabled; - String signalContentDescription; - int dataTypeIconId; - String dataContentDescription; boolean activityIn; boolean activityOut; - String enabledDesc; boolean noSim; - boolean isDataTypeIconWide; boolean roaming; } private final class CellSignalCallback implements SignalCallback { private final CallbackInfo mInfo = new CallbackInfo(); - @Override - public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description, boolean isTransient) { - mInfo.wifiEnabled = enabled; - refreshState(mInfo); - } @Override - public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) { - if (qsIcon == null) { + public void setMobileDataIndicators(IconState statusIcon, int statusType, + boolean activityIn, boolean activityOut, String typeContentDescription, + int subId, boolean roaming, boolean isEmergency) { + if (statusIcon == null) { // Not data sim, don't display. return; } - mInfo.enabled = qsIcon.visible; - mInfo.signalContentDescription = qsIcon.contentDescription; - mInfo.dataTypeIconId = qsType; - mInfo.dataContentDescription = typeContentDescription; + mInfo.enabled = statusIcon.visible; mInfo.activityIn = activityIn; mInfo.activityOut = activityOut; - mInfo.enabledDesc = description; - mInfo.isDataTypeIconWide = qsType != 0 && isWide; mInfo.roaming = roaming; refreshState(mInfo); } @@ -206,15 +189,6 @@ public class CellularTile extends QSTileImpl<SignalState> { @Override public void setNoSims(boolean show) { mInfo.noSim = show; - if (mInfo.noSim) { - // Make sure signal gets cleared out when no sims. - mInfo.dataTypeIconId = 0; - // Show a No SIMs description to avoid emergency calls message. - mInfo.enabled = true; - mInfo.enabledDesc = mContext.getString( - R.string.keyguard_missing_sim_message_short); - mInfo.signalContentDescription = mInfo.enabledDesc; - } refreshState(mInfo); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index ccebd7ceec32..429ace62ad16 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -65,7 +65,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.ArraySet; -import android.util.LauncherIcons; +import android.util.IconDrawableFactory; import android.util.Log; import android.util.MutableBoolean; import android.view.Display; @@ -121,6 +121,7 @@ public class SystemServicesProxy { ActivityManager mAm; IActivityManager mIam; PackageManager mPm; + IconDrawableFactory mDrawableFactory; IPackageManager mIpm; AssistUtils mAssistUtils; WindowManager mWm; @@ -139,7 +140,6 @@ public class SystemServicesProxy { int mDummyThumbnailHeight; Paint mBgProtectionPaint; Canvas mBgProtectionCanvas; - LauncherIcons mLauncherIcons; private final Handler mHandler = new H(); @@ -258,6 +258,7 @@ public class SystemServicesProxy { mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); mIam = ActivityManager.getService(); mPm = context.getPackageManager(); + mDrawableFactory = IconDrawableFactory.newInstance(context); mIpm = AppGlobals.getPackageManager(); mAssistUtils = new AssistUtils(context); mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); @@ -296,8 +297,6 @@ public class SystemServicesProxy { Collections.addAll(sRecentsBlacklist, res.getStringArray(R.array.recents_blacklist_array)); - - mLauncherIcons = new LauncherIcons(context); } /** @@ -808,13 +807,19 @@ public class SystemServicesProxy { * Returns the content description for a given task, badging it if necessary. The content * description joins the app and activity labels. */ - public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) { + public String getBadgedContentDescription(ActivityInfo info, int userId, + ActivityManager.TaskDescription td, Resources res) { // If we are mocking, then return a mock label if (RecentsDebugFlags.Static.EnableMockTasks) { return "Recent Task Content Description: " + userId; } - String activityLabel = info.loadLabel(mPm).toString(); + String activityLabel; + if (td != null && td.getLabel() != null) { + activityLabel = td.getLabel(); + } else { + activityLabel = info.loadLabel(mPm).toString(); + } String applicationLabel = info.applicationInfo.loadLabel(mPm).toString(); String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); return applicationLabel.equals(activityLabel) ? badgedApplicationLabel @@ -834,8 +839,7 @@ public class SystemServicesProxy { return new ColorDrawable(0xFF666666); } - Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(info.loadIcon(mPm)); - return getBadgedIcon(icon, userId); + return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId); } /** @@ -850,8 +854,7 @@ public class SystemServicesProxy { return new ColorDrawable(0xFF666666); } - Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(appInfo.loadIcon(mPm)); - return getBadgedIcon(icon, userId); + return mDrawableFactory.getBadgedIcon(appInfo, userId); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 5c25bfd6441c..78c71a11f3f5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -182,7 +182,8 @@ public class RecentsTaskLoadPlan { // Load the title, icon, and color ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey); String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription); - String titleDescription = loader.getAndUpdateContentDescription(taskKey, res); + String titleDescription = loader.getAndUpdateContentDescription(taskKey, + t.taskDescription, res); String dismissDescription = String.format(dismissDescFormat, titleDescription); String appInfoDescription = String.format(appInfoDescFormat, titleDescription); Drawable icon = isStackTask diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index e8ffb9150da7..5e78b61e021c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -449,7 +449,8 @@ public class RecentsTaskLoader { * Returns the cached task content description if the task key is not expired, updating the * cache if it is. */ - String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) { + String getAndUpdateContentDescription(Task.TaskKey taskKey, ActivityManager.TaskDescription td, + Resources res) { SystemServicesProxy ssp = Recents.getSystemServices(); // Return the cached content description if it exists @@ -461,8 +462,15 @@ public class RecentsTaskLoader { // All short paths failed, load the label from the activity info and cache it ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); if (activityInfo != null) { - label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, res); - mContentDescriptionCache.put(taskKey, label); + label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, td, res); + if (td == null) { + // Only add to the cache if the task description is null, otherwise, it is possible + // for the task description to change between calls without the last active time + // changing (ie. between preloading and Overview starting) which would lead to stale + // content descriptions + // TODO: Investigate improving this + mContentDescriptionCache.put(taskKey, label); + } return label; } // If the content description does not exist, return an empty label for now, but do not diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 677642e53d6e..dff09bda8844 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -1115,6 +1115,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationInflater.setInflateExceptionHandler(inflateExceptionHandler); } + public void setNeedsRedaction(boolean needsRedaction) { + mNotificationInflater.setRedactAmbient(needsRedaction); + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } @@ -1490,7 +1494,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return getActualHeight(); } if (mGuts != null && mGuts.isExposed()) { - return mGuts.getHeight(); + return mGuts.getIntrinsicHeight(); } else if ((isChildInGroup() && !isGroupExpanded())) { return mPrivateLayout.getMinHeight(); } else if (mShowAmbient) { @@ -1831,7 +1835,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public int getMinHeight() { - if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { + if (mGuts != null && mGuts.isExposed()) { + return mGuts.getIntrinsicHeight(); + } else if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { return getPinnedHeadsUpHeight(false /* atLeastMinHeight */); } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) { return mChildrenContainer.getMinHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 8f160dc0384e..609856522e75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -428,6 +428,9 @@ public class NotificationContentView extends FrameLayout { mAmbientChild.animate().cancel(); removeView(mAmbientChild); } + if (child == null) { + return; + } addView(child); mAmbientChild = child; mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index 2713f58272d0..137531079a12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -39,6 +39,7 @@ import android.util.Log; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -73,7 +74,8 @@ public class NotificationGuts extends FrameLayout { private Handler mHandler; private Runnable mFalsingCheck; private boolean mNeedsFalsingProtection; - private OnGutsClosedListener mListener; + private OnGutsClosedListener mClosedListener; + private OnHeightChangedListener mHeightListener; private GutsContent mGutsContent; @@ -87,6 +89,11 @@ public class NotificationGuts extends FrameLayout { public View getContentView(); /** + * @return the actual height of the content. + */ + public int getActualHeight(); + + /** * Called when the guts view have been told to close, typically after an outside * interaction. Returning {@code true} here will prevent the guts view to close. */ @@ -102,6 +109,10 @@ public class NotificationGuts extends FrameLayout { public void onGutsClosed(NotificationGuts guts); } + public interface OnHeightChangedListener { + public void onHeightChanged(NotificationGuts guts); + } + interface OnSettingsClickListener { void onClick(View v, int appUid); } @@ -125,7 +136,6 @@ public class NotificationGuts extends FrameLayout { public NotificationGuts(Context context) { this(context, null); - } public void setGutsContent(GutsContent content) { @@ -189,8 +199,8 @@ public class NotificationGuts extends FrameLayout { public void closeControls(int x, int y, boolean save) { if (getWindowToken() == null) { - if (mListener != null) { - mListener.onGutsClosed(this); + if (mClosedListener != null) { + mClosedListener.onGutsClosed(this); } return; } @@ -198,8 +208,8 @@ public class NotificationGuts extends FrameLayout { animateClose(x, y); } setExposed(false, mNeedsFalsingProtection); - if (mListener != null) { - mListener.onGutsClosed(this); + if (mClosedListener != null) { + mClosedListener.onGutsClosed(this); } } @@ -234,6 +244,10 @@ public class NotificationGuts extends FrameLayout { return mActualHeight; } + public int getIntrinsicHeight() { + return mGutsContent != null && mExposed ? mGutsContent.getActualHeight() : getHeight(); + } + public void setClipTopAmount(int clipTopAmount) { mClipTopAmount = clipTopAmount; invalidate(); @@ -251,10 +265,21 @@ public class NotificationGuts extends FrameLayout { } public void setClosedListener(OnGutsClosedListener listener) { - mListener = listener; + mClosedListener = listener; + } + + public void setHeightChangedListener(OnHeightChangedListener listener) { + mHeightListener = listener; + } + + protected void onHeightChanged() { + if (mHeightListener != null) { + mHeightListener.onHeightChanged(this); + } } public void setExposed(boolean exposed, boolean needsFalsingProtection) { + final boolean wasExposed = mExposed; mExposed = exposed; mNeedsFalsingProtection = needsFalsingProtection; if (mExposed && mNeedsFalsingProtection) { @@ -262,6 +287,13 @@ public class NotificationGuts extends FrameLayout { } else { mHandler.removeCallbacks(mFalsingCheck); } + if (wasExposed != mExposed && mGutsContent != null) { + final View contentView = mGutsContent.getContentView(); + contentView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (mExposed) { + contentView.requestAccessibilityFocus(); + } + } } public boolean willBeRemoved() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 7928575df4d4..f3c7b8495a01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -35,6 +35,7 @@ import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Switch; @@ -57,6 +58,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private INotificationManager mINotificationManager; private String mPkg; + private String mAppName; private int mAppUid; private List<NotificationChannel> mNotificationChannels; private NotificationChannel mSingleNotificationChannel; @@ -125,7 +127,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - String appName = mPkg; + mAppName = mPkg; Drawable pkgicon = null; CharSequence channelNameText = ""; ApplicationInfo info = null; @@ -137,7 +139,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G | PackageManager.MATCH_DIRECT_BOOT_AWARE); if (info != null) { mAppUid = info.uid; - appName = String.valueOf(pm.getApplicationLabel(info)); + mAppName = String.valueOf(pm.getApplicationLabel(info)); pkgicon = pm.getApplicationIcon(info); } } catch (PackageManager.NameNotFoundException e) { @@ -189,7 +191,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } else { channelNameText = mSingleNotificationChannel.getName(); } - ((TextView) findViewById(R.id.pkgname)).setText(appName); + ((TextView) findViewById(R.id.pkgname)).setText(mAppName); ((TextView) findViewById(R.id.channel_name)).setText(channelNameText); // Set group information if this channel has an associated group. @@ -309,6 +311,21 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G }); } + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mGutsContainer != null && + event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + if (mGutsContainer.isExposed()) { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_opened_accessibility, mAppName)); + } else { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_closed_accessibility, mAppName)); + } + } + } + private void updateSecondaryText() { final boolean disabled = mSingleNotificationChannel != null && getSelectedImportance() == IMPORTANCE_NONE; @@ -386,4 +403,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } return false; } + + @Override + public int getActualHeight() { + return getHeight(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java index fee24b7b7645..7563fd1bf9bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java @@ -227,6 +227,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl if (mShouldShowMenu && !NotificationStackScrollLayout.isPinnedHeadsUp(view) && !mParent.areGutsExposed() + && !mParent.isDark() && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) { // Only show the menu if we're not a heads up view and guts aren't exposed. mCheckForDrag = new CheckForDrag(); @@ -316,6 +317,10 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } private void dismiss(View animView, float velocity) { + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + } + mHandler.removeCallbacks(mCheckForDrag); mMenuSnappedTo = false; mDismissing = true; mSwipeHelper.dismiss(animView, velocity); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java index 4a3f112f0406..ccc99c59e510 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java @@ -21,35 +21,47 @@ import java.util.List; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; -import android.graphics.Color; +import android.graphics.Typeface; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; +import android.text.SpannableString; +import android.text.style.StyleSpan; import android.util.AttributeSet; import android.util.Log; -import android.util.TypedValue; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; + +import com.android.systemui.Interpolators; import com.android.systemui.R; public class NotificationSnooze extends LinearLayout implements NotificationGuts.GutsContent, View.OnClickListener { - private static final int MAX_ASSISTANT_SUGGESTIONS = 2; + private static final int MAX_ASSISTANT_SUGGESTIONS = 1; private NotificationGuts mGutsContainer; private NotificationSwipeActionHelper mSnoozeListener; private StatusBarNotification mSbn; private TextView mSelectedOptionText; private TextView mUndoButton; - private ViewGroup mSnoozeOptionView; + private ImageView mExpandButton; + private View mDivider; + private ViewGroup mSnoozeOptionContainer; private List<SnoozeOption> mSnoozeOptions; - private boolean mSnoozing; + private int mCollapsedHeight; private SnoozeOption mSelectedOption; + private boolean mSnoozing; + private boolean mExpanded; + private AnimatorSet mExpandAnimation; public NotificationSnooze(Context context, AttributeSet attrs) { super(context, attrs); @@ -58,16 +70,21 @@ public class NotificationSnooze extends LinearLayout @Override protected void onFinishInflate() { super.onFinishInflate(); + mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height); + findViewById(R.id.notification_snooze).setOnClickListener(this); + mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default); + mUndoButton = (TextView) findViewById(R.id.undo); + mUndoButton.setOnClickListener(this); + mExpandButton = (ImageView) findViewById(R.id.expand_button); + mDivider = findViewById(R.id.divider); + mDivider.setAlpha(0f); + mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options); + mSnoozeOptionContainer.setAlpha(0f); + // Create the different options based on list mSnoozeOptions = getDefaultSnoozeOptions(); createOptionViews(); - // Snackbar - mSelectedOptionText = findViewById(R.id.snooze_option_default); - mSelectedOptionText.setOnClickListener(this); - mUndoButton = findViewById(R.id.undo); - mUndoButton.setOnClickListener(this); - // Default to first option in list setSelected(mSnoozeOptions.get(0)); } @@ -96,52 +113,68 @@ public class NotificationSnooze extends LinearLayout private SnoozeOption createOption(int descriptionResId, int minutes) { Resources res = getResources(); - String resultText = String.format( - res.getString(R.string.snoozed_for_time), res.getString(descriptionResId)); - return new SnoozeOption(null, minutes, res.getString(descriptionResId), resultText); + final String description = res.getString(descriptionResId); + String resultText = String.format(res.getString(R.string.snoozed_for_time), description); + SpannableString string = new SpannableString(resultText); + string.setSpan(new StyleSpan(Typeface.BOLD), + resultText.length() - description.length(), resultText.length(), 0 /* flags */); + return new SnoozeOption(null, minutes, res.getString(descriptionResId), string); } private void createOptionViews() { - mSnoozeOptionView = findViewById(R.id.snooze_options); - mSnoozeOptionView.removeAllViews(); - mSnoozeOptionView.setVisibility(View.GONE); - final Resources res = getResources(); - final int textSize = res.getDimensionPixelSize(R.dimen.snooze_option_text_size); - final int p = res.getDimensionPixelSize(R.dimen.snooze_option_padding); - - // Add all the options + mSnoozeOptionContainer.removeAllViews(); + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); for (int i = 0; i < mSnoozeOptions.size(); i++) { SnoozeOption option = mSnoozeOptions.get(i); - TextView tv = new TextView(getContext()); - tv.setTextColor(Color.WHITE); - tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - tv.setPadding(p, p, p, p); - mSnoozeOptionView.addView(tv); + TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option, + mSnoozeOptionContainer, false); + mSnoozeOptionContainer.addView(tv); tv.setText(option.description); tv.setTag(option); tv.setOnClickListener(this); } + } - // Add the undo option as final item - TextView tv = new TextView(getContext()); - tv.setTextColor(Color.WHITE); - tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - tv.setPadding(p, p, p, p); - mSnoozeOptionView.addView(tv); - tv.setText(R.string.snooze_option_dont_snooze); - tv.setOnClickListener(this); + private void hideSelectedOption() { + final int childCount = mSnoozeOptionContainer.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = mSnoozeOptionContainer.getChildAt(i); + child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE); + } } private void showSnoozeOptions(boolean show) { - mSelectedOptionText.setVisibility(show ? View.GONE : View.VISIBLE); - mUndoButton.setVisibility(show ? View.GONE : View.VISIBLE); - mSnoozeOptionView.setVisibility(show ? View.VISIBLE : View.GONE); + mExpanded = show; + animateSnoozeOptions(show); + int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification + : com.android.internal.R.drawable.ic_expand_notification; + mExpandButton.setImageResource(drawableId); + if (mGutsContainer != null) { + mGutsContainer.onHeightChanged(); + } + } + + private void animateSnoozeOptions(boolean show) { + if (mExpandAnimation != null) { + mExpandAnimation.cancel(); + } + ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA, + mDivider.getAlpha(), show ? 1f : 0f); + ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA, + mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f); + mExpandAnimation = new AnimatorSet(); + mExpandAnimation.playTogether(dividerAnim, optionAnim); + mExpandAnimation.setDuration(150); + mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); + mExpandAnimation.start(); } private void setSelected(SnoozeOption option) { mSelectedOption = option; mSelectedOptionText.setText(option.confirmation); showSnoozeOptions(false); + hideSelectedOption(); } @Override @@ -153,17 +186,28 @@ public class NotificationSnooze extends LinearLayout final SnoozeOption tag = (SnoozeOption) v.getTag(); if (tag != null) { setSelected(tag); - } else if (id == R.id.snooze_option_default) { - // Show more snooze options - showSnoozeOptions(true); + } else if (id == R.id.notification_snooze) { + // Toggle snooze options + showSnoozeOptions(!mExpanded); } else { - undoSnooze(); + // Undo snooze was selected + mSelectedOption = null; + int[] parentLoc = new int[2]; + int[] targetLoc = new int[2]; + mGutsContainer.getLocationOnScreen(parentLoc); + v.getLocationOnScreen(targetLoc); + final int centerX = v.getWidth() / 2; + final int centerY = v.getHeight() / 2; + final int x = targetLoc[0] - parentLoc[0] + centerX; + final int y = targetLoc[1] - parentLoc[1] + centerY; + showSnoozeOptions(false); + mGutsContainer.closeControls(x, y, false /* save */); } } - private void undoSnooze() { - mSelectedOption = null; - mGutsContainer.closeControls(-1 /* x */, -1 /* y */, true /* notify */); + @Override + public int getActualHeight() { + return mExpanded ? getHeight() : mCollapsedHeight; } @Override @@ -173,6 +217,8 @@ public class NotificationSnooze extends LinearLayout @Override public View getContentView() { + // Reset the view before use + setSelected(mSnoozeOptions.get(0)); return this; } @@ -197,11 +243,8 @@ public class NotificationSnooze extends LinearLayout mSnoozing = true; mSnoozeListener.snooze(mSbn, mSelectedOption); return true; - } else { - // Reset the view once it's closed - setSelected(mSnoozeOptions.get(0)); - showSnoozeOptions(false); } + // The view should be closed return false; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index dc254f9fdc1e..b01d9cc07bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -24,8 +24,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Rect; -import android.graphics.drawable.Animatable; -import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.telephony.SubscriptionInfo; @@ -120,6 +118,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController private boolean mBlockWifi; private boolean mBlockEthernet; private boolean mActivityEnabled; + private boolean mForceBlockWifi; public SignalClusterView(Context context) { this(context, null); @@ -151,6 +150,16 @@ public class SignalClusterView extends LinearLayout implements NetworkController updateActivityEnabled(); } + public void setForceBlockWifi() { + mForceBlockWifi = true; + mBlockWifi = true; + if (isAttachedToWindow()) { + // Re-register to get new callbacks. + mNetworkController.removeCallback(this); + mNetworkController.addCallback(this); + } + } + @Override public void onTuningChanged(String key, String newValue) { if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { @@ -167,7 +176,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController mBlockAirplane = blockAirplane; mBlockMobile = blockMobile; mBlockEthernet = blockEthernet; - mBlockWifi = blockWifi; + mBlockWifi = blockWifi || mForceBlockWifi; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -288,9 +297,9 @@ public class SignalClusterView extends LinearLayout implements NetworkController } @Override - public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) { + public void setMobileDataIndicators(IconState statusIcon, int statusType, + boolean activityIn, boolean activityOut, String typeContentDescription, + int subId, boolean roaming, boolean isEmergency) { PhoneState state = getState(subId); if (state == null) { return; @@ -300,7 +309,6 @@ public class SignalClusterView extends LinearLayout implements NetworkController state.mMobileTypeId = statusType; state.mMobileDescription = statusIcon.contentDescription; state.mMobileTypeDescription = typeContentDescription; - state.mIsMobileTypeIconWide = statusType != 0 && isWide; state.mRoaming = roaming; state.mActivityIn = activityIn && mActivityEnabled; state.mActivityOut = activityOut && mActivityEnabled; @@ -525,7 +533,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController mWifiAirplaneSpacer.setVisibility(View.GONE); } - if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) { + if (((anyMobileVisible && firstMobileTypeId == 0) || mNoSimsVisible) && mWifiVisible) { mWifiSignalSpacer.setVisibility(View.VISIBLE); } else { mWifiSignalSpacer.setVisibility(View.GONE); @@ -636,7 +644,6 @@ public class SignalClusterView extends LinearLayout implements NetworkController private int mMobileStrengthId = 0, mMobileTypeId = 0; private int mLastMobileStrengthId = -1; private int mLastMobileTypeId = -1; - private boolean mIsMobileTypeIconWide; private String mMobileDescription, mMobileTypeDescription; private ViewGroup mMobileGroup; @@ -692,12 +699,8 @@ public class SignalClusterView extends LinearLayout implements NetworkController // When this isn't next to wifi, give it some extra padding between the signals. mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0, 0, 0, 0); - mMobile.setPaddingRelative( - mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding, - 0, 0, 0); - mMobileDark.setPaddingRelative( - mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding, - 0, 0, 0); + mMobile.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0); + mMobileDark.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0); if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d", (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index ec15d10ac351..263df79318ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -459,9 +459,13 @@ public class StatusBarIconView extends AnimatedImageView { } CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE); + CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT); CharSequence ticker = n.tickerText; - CharSequence desc = !TextUtils.isEmpty(title) ? title + // Some apps just put the app name into the title + CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title; + + CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText : !TextUtils.isEmpty(ticker) ? ticker : ""; return c.getString(R.string.accessibility_desc_notification_icon, appName, desc); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java index 1ffc94480dab..0b9244a50718 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java @@ -29,6 +29,7 @@ import android.view.animation.PathInterpolator; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.widget.NotificationExpandButton; import com.android.systemui.Interpolators; import com.android.systemui.ViewInvertHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -54,7 +55,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected int mColor; private ImageView mIcon; - private ImageView mExpandButton; + private NotificationExpandButton mExpandButton; private NotificationHeaderView mNotificationHeader; private TextView mHeaderText; private ImageView mWorkProfileImage; @@ -106,13 +107,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } protected void resolveHeaderViews() { - mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon); - mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text); - mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button); - mWorkProfileImage = (ImageView) mView.findViewById(com.android.internal.R.id.profile_badge); + mIcon = mView.findViewById(com.android.internal.R.id.icon); + mHeaderText = mView.findViewById(com.android.internal.R.id.header_text); + mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); + mExpandButton.setLabeledBy(mRow); + mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mColor = resolveColor(mExpandButton); - mNotificationHeader = (NotificationHeaderView) mView.findViewById( - com.android.internal.R.id.notification_header); + mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); getDozer().setColor(mColor); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java index 73eecbbf2728..2e34f2483a85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java @@ -21,6 +21,8 @@ import android.content.Context; import android.service.notification.StatusBarNotification; import android.util.Log; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; @@ -49,6 +51,7 @@ public class NotificationInflater { private RemoteViews.OnClickHandler mRemoteViewClickHandler; private boolean mIsChildInGroup; private InflationExceptionHandler mInflateExceptionHandler; + private boolean mRedactAmbient; public NotificationInflater(ExpandableNotificationRow row) { mRow = row; @@ -92,6 +95,21 @@ public class NotificationInflater { mRemoteViewClickHandler = remoteViewClickHandler; } + public void setRedactAmbient(boolean redactAmbient) { + if (mRedactAmbient != redactAmbient) { + mRedactAmbient = redactAmbient; + if (mRow.getEntry() == null) { + return; + } + try { + inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW); + } catch (InflationException e) { + mInflateExceptionHandler.handleInflationException( + mRow.getStatusBarNotification(), e); + } + } + } + public void inflateNotificationViews() throws InflationException { inflateNotificationViews(FLAG_REINFLATE_ALL); } @@ -123,6 +141,8 @@ public class NotificationInflater { Notification.Builder builder, Context packageContext) { NotificationData.Entry entry = mRow.getEntry(); NotificationContentView privateLayout = mRow.getPrivateLayout(); + NotificationContentView publicLayout = mRow.getPublicLayout(); + boolean isLowPriority = mIsLowPriority && !mIsChildInGroup; if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) { final RemoteViews newContentView = createContentView(builder, @@ -190,7 +210,6 @@ public class NotificationInflater { } if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) { - NotificationContentView publicLayout = mRow.getPublicLayout(); final RemoteViews newPublicNotification = builder.makePublicContentView(); if (!compareRemoteViews(newPublicNotification, entry.cachedPublicContentView)) { @@ -209,18 +228,24 @@ public class NotificationInflater { } if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) { - final RemoteViews newAmbientNotification - = builder.makeAmbientNotification(); - if (!compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) { + final RemoteViews newAmbientNotification = mRedactAmbient + ? builder.makePublicAmbientNotification() + : builder.makeAmbientNotification(); + NotificationContentView newParent = mRedactAmbient ? publicLayout : privateLayout; + NotificationContentView otherParent = !mRedactAmbient ? publicLayout : privateLayout; + + if (newParent.getAmbientChild() == null || + !compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) { View ambientContentView = newAmbientNotification.apply( packageContext, - privateLayout, + newParent, mRemoteViewClickHandler); ambientContentView.setIsRootNamespace(true); - privateLayout.setAmbientChild(ambientContentView); + newParent.setAmbientChild(ambientContentView); + otherParent.setAmbientChild(null); } else { newAmbientNotification.reapply(packageContext, - privateLayout.getAmbientChild(), + newParent.getAmbientChild(), mRemoteViewClickHandler); } entry.cachedAmbientContentView = newAmbientNotification; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java index 92069142a61f..f216d6c4480f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java @@ -83,7 +83,7 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { /** * How much faster we collapse the lockscreen when authenticating with fingerprint. */ - private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.3f; + private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f; private PowerManager mPowerManager; private Handler mHandler = new Handler(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index f1b449891529..8a97be5864d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -2022,7 +2022,7 @@ public class NotificationPanelView extends PanelView implements @Override protected boolean shouldUseDismissingAnimation() { return mStatusBarState != StatusBarState.SHADE - && !mStatusBar.isKeyguardCurrentlySecure(); + && (!mStatusBar.isKeyguardCurrentlySecure() || !isTracking()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 48a8329d085e..e86fd4849fec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -725,8 +725,14 @@ public abstract class PanelView extends FrameLayout { } } else { if (shouldUseDismissingAnimation()) { - mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel, - getHeight()); + if (vel == 0) { + animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); + long duration = (long) (200 + mExpandedHeight / getHeight() * 100); + animator.setDuration(duration); + } else { + mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel, + getHeight()); + } } else { mFlingAnimationUtilsClosing .apply(animator, mExpandedHeight, target, vel, getHeight()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java index a9eb20ba3a57..6361eb6ccb89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java @@ -57,12 +57,12 @@ public class SignalDrawable extends Drawable { private static final int LEVEL_MASK = 0xff; private static final int NUM_LEVEL_SHIFT = 8; private static final int NUM_LEVEL_MASK = 0xff << NUM_LEVEL_SHIFT; - private static final int STATE_SHIFT = 16; - private static final int STATE_MASK = 0xff << STATE_SHIFT; - private static final int STATE_NONE = 0; - private static final int STATE_EMPTY = 1; - private static final int STATE_CUT = 2; - private static final int STATE_CARRIER_CHANGE = 3; + public static final int STATE_SHIFT = 16; + public static final int STATE_MASK = 0xff << STATE_SHIFT; + public static final int STATE_NONE = 0; + public static final int STATE_EMPTY = 1; + public static final int STATE_CUT = 2; + public static final int STATE_CARRIER_CHANGE = 3; private static final long DOT_DELAY = 1000; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index f0d38b2e3d3c..46d64153d04d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -31,6 +31,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCE import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; +import android.R.style; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; @@ -48,10 +49,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; -import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -95,6 +94,7 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.Display; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -139,7 +139,6 @@ import com.android.systemui.doze.DozeLog; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.PluginFragmentListener; import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.pip.phone.PipManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; @@ -1337,7 +1336,10 @@ public class StatusBar extends SystemUI implements DemoMode, } private void inflateDismissView() { - mDismissView = (DismissView) LayoutInflater.from(mContext).inflate( + // Always inflate with a dark theme, since this sits on the scrim. + ContextThemeWrapper themedContext = new ContextThemeWrapper(mContext, + style.Theme_DeviceDefault); + mDismissView = (DismissView) LayoutInflater.from(themedContext).inflate( R.layout.status_bar_notification_dismiss_all, mStackScroller, false); mDismissView.setOnButtonClickListener(new View.OnClickListener() { @Override @@ -1809,6 +1811,7 @@ public class StatusBar extends SystemUI implements DemoMode, updatePublicContentView(ent, ent.notification); } ent.row.setSensitive(sensitive, deviceSensitive); + ent.row.setNeedsRedaction(needsRedaction(ent)); if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) { ExpandableNotificationRow summary = mGroupManager.getGroupSummary( ent.row.getStatusBarNotification()); @@ -1897,6 +1900,21 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationIconAreaController.updateNotificationIcons(mNotificationData); } + /** @return true if the entry needs redaction when on the lockscreen. */ + private boolean needsRedaction(Entry ent) { + int userId = ent.notification.getUserId(); + + boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId); + boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId); + boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction; + + boolean notificationRequestsRedaction = + ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE; + boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey()); + + return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen; + } + /** * Disable QS if device not provisioned. * If the user switcher is simple then disable QS during setup because @@ -1907,6 +1925,7 @@ public class StatusBar extends SystemUI implements DemoMode, && (mUserSetup || mUserSwitcherController == null || !mUserSwitcherController.isSimpleUserSwitcher()) && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) + && !mDozing && !ONLY_CORE_APPS); } @@ -4312,6 +4331,7 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.setDozing(mDozing); mKeyguardIndicationController.setDozing(mDozing); mNotificationPanel.setDark(mDozing); + updateQsExpansionEnabled(); // Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock // for pulsing so the Keyguard fade-out animation scrim can take over. @@ -5754,6 +5774,9 @@ public class StatusBar extends SystemUI implements DemoMode, snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper()); snoozeGuts.setStatusBarNotification(sbn); snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria); + guts.setHeightChangedListener((NotificationGuts g) -> { + mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */); + }); } if (gutsView instanceof NotificationInfo) { @@ -5855,6 +5878,9 @@ public class StatusBar extends SystemUI implements DemoMode, } final ExpandableNotificationRow row = (ExpandableNotificationRow) v; + if (row.isDark()) { + return false; + } bindGuts(row, item); NotificationGuts guts = row.getGuts(); @@ -5902,8 +5928,10 @@ public class StatusBar extends SystemUI implements DemoMode, } }); a.start(); - guts.setExposed(true /* exposed */, - mState == StatusBarState.KEYGUARD /* needsFalsingProtection */); + final boolean needsFalsingProtection = + (mState == StatusBarState.KEYGUARD && + !mAccessibilityManager.isTouchExplorationEnabled()); + guts.setExposed(true /* exposed */, needsFalsingProtection); row.closeRemoteInput(); mStackScroller.onHeightChanged(row, true /* needsAnimation */); mNotificationGutsExposed = guts; @@ -6154,6 +6182,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } + row.setNeedsRedaction(needsRedaction(entry)); boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey()); row.setIsLowPriority(isLowPriority); // bind the click event to the content area @@ -6518,7 +6547,6 @@ public class StatusBar extends SystemUI implements DemoMode, NotificationData.Entry entry = new NotificationData.Entry(sbn); Dependency.get(LeakDetector.class).trackInstance(entry); entry.createIcons(mContext, sbn); - // Construct the expanded view. inflateViews(entry, mStackScroller); return entry; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index a456786d712f..e98dc987aa5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -110,30 +110,24 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa public void setWifiIndicators(final boolean enabled, final IconState statusIcon, final IconState qsIcon, final boolean activityIn, final boolean activityOut, final String description, boolean isTransient) { - post(new Runnable() { - @Override - public void run() { - for (SignalCallback callback : mSignalCallbacks) { - callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, - description, isTransient); - } + post(() -> { + for (SignalCallback callback : mSignalCallbacks) { + callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, + description, isTransient); } }); } @Override - public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon, - final int statusType, final int qsType,final boolean activityIn, + public void setMobileDataIndicators(final IconState statusIcon, + final int statusType, final boolean activityIn, final boolean activityOut, final String typeContentDescription, - final String description, final boolean isWide, final int subId, boolean roaming) { - post(new Runnable() { - @Override - public void run() { - for (SignalCallback signalCluster : mSignalCallbacks) { - signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, - activityIn, activityOut, typeContentDescription, description, isWide, - subId, roaming); - } + final int subId, boolean roaming, boolean isEmergency) { + post(() -> { + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setMobileDataIndicators(statusIcon, statusType, + activityIn, activityOut, typeContentDescription, + subId, roaming, isEmergency); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 67b5596e34c9..4421a6ab0cfd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -244,7 +244,8 @@ public class MobileSignalController extends SignalController< return SignalDrawable.getCarrierChangeState(getNumLevels()); } else if (mCurrentState.connected) { return SignalDrawable.getState(mCurrentState.level, getNumLevels(), - mCurrentState.inetCondition == 0); + mCurrentState.inetCondition == 0 || + (mCurrentState.dataDisabled && mCurrentState.userSetup)); } else if (mCurrentState.enabled) { return SignalDrawable.getEmptyState(getNumLevels()); } else { @@ -263,24 +264,14 @@ public class MobileSignalController extends SignalController< String contentDescription = getStringIfExists(getContentDescription()); String dataContentDescription = getStringIfExists(icons.mDataContentDescription); - final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED + final boolean dataDisabled = mCurrentState.dataDisabled && mCurrentState.userSetup; // Show icon in QS when we are connected or data is disabled. - boolean showDataIcon = mCurrentState.dataConnected || dataDisabled; + boolean showDataIcon = mCurrentState.dataConnected; IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode, getCurrentIconId(), contentDescription); - int qsTypeIcon = 0; - IconState qsIcon = null; - String description = null; - // Only send data sim callbacks to QS. - if (mCurrentState.dataSim) { - qsTypeIcon = showDataIcon ? icons.mQsDataType : 0; - qsIcon = new IconState(mCurrentState.enabled - && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription); - description = mCurrentState.isEmergency ? null : mCurrentState.networkName; - } boolean activityIn = mCurrentState.dataConnected && !mCurrentState.carrierNetworkChangeMode && mCurrentState.activityIn; @@ -289,9 +280,10 @@ public class MobileSignalController extends SignalController< && mCurrentState.activityOut; showDataIcon &= mCurrentState.isDefault || dataDisabled; int typeIcon = showDataIcon ? icons.mDataType : 0; - callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, - activityIn, activityOut, dataContentDescription, description, icons.mIsWide, - mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming); + callback.setMobileDataIndicators(statusIcon, typeIcon, + activityIn, activityOut, dataContentDescription, + mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming, + mCurrentState.isEmergency); } @Override @@ -438,14 +430,14 @@ public class MobileSignalController extends SignalController< } else { mCurrentState.iconGroup = mDefaultIcons; } + mCurrentState.dataDisabled = isDataDisabled(); mCurrentState.dataConnected = mCurrentState.connected - && mDataState == TelephonyManager.DATA_CONNECTED; + && mDataState == TelephonyManager.DATA_CONNECTED + && !mCurrentState.dataDisabled; mCurrentState.roaming = isRoaming(); if (isCarrierNetworkChangeActive()) { mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; - } else if (isDataDisabled()) { - mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED; } if (isEmergencyOnly() != mCurrentState.isEmergency) { mCurrentState.isEmergency = isEmergencyOnly(); @@ -577,6 +569,7 @@ public class MobileSignalController extends SignalController< boolean isDefault; boolean userSetup; boolean roaming; + boolean dataDisabled; @Override public void copyFrom(State s) { @@ -592,6 +585,7 @@ public class MobileSignalController extends SignalController< carrierNetworkChangeMode = state.carrierNetworkChangeMode; userSetup = state.userSetup; roaming = state.roaming; + dataDisabled = state.dataDisabled; } @Override @@ -609,6 +603,7 @@ public class MobileSignalController extends SignalController< builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode) .append(','); builder.append("userSetup=").append(userSetup); + builder.append("dataDisabled=").append(dataDisabled); } @Override @@ -623,6 +618,7 @@ public class MobileSignalController extends SignalController< && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode && ((MobileState) o).userSetup == userSetup && ((MobileState) o).isDefault == isDefault + && ((MobileState) o).dataDisabled == dataDisabled && ((MobileState) o).roaming == roaming; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index c02ce0ea6335..ab4a8f21ba24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -48,9 +48,9 @@ public interface NetworkController extends CallbackController<SignalCallback>, D default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, boolean activityIn, boolean activityOut, String description, boolean isTransient) {} - default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) {} + default void setMobileDataIndicators(IconState statusIcon, int statusType, + boolean activityIn, boolean activityOut, String typeContentDescription, + int subId, boolean roaming, boolean isEmergency) {} default void setSubs(List<SubscriptionInfo> subs) {} default void setNoSims(boolean show) {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index c21f444d0bf3..60f4ab8ffe0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -867,7 +867,6 @@ public class NetworkControllerImpl extends BroadcastReceiver datatype.equals("h") ? TelephonyIcons.H : datatype.equals("lte") ? TelephonyIcons.LTE : datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS : - datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED : TelephonyIcons.UNKNOWN; } if (args.containsKey("roam")) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index 3f8e41ae39d5..1fb9b691d525 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -23,16 +23,20 @@ public interface SecurityController extends CallbackController<SecurityControlle /** Whether the device has device owner, even if not on this user. */ boolean isDeviceManaged(); boolean hasProfileOwner(); + boolean hasWorkProfile(); String getDeviceOwnerName(); String getProfileOwnerName(); CharSequence getDeviceOwnerOrganizationName(); + CharSequence getWorkProfileOrganizationName(); boolean isNetworkLoggingEnabled(); boolean isVpnEnabled(); boolean isVpnRestricted(); /** Whether the VPN app should use branded VPN iconography. */ boolean isVpnBranded(); String getPrimaryVpnName(); - String getProfileVpnName(); + String getWorkProfileVpnName(); + boolean hasCACertInCurrentUser(); + boolean hasCACertInWorkProfile(); void onUserSwitched(int newUserId); public interface SecurityControllerCallback { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 19ced235dc1e..fcb728939e88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -138,6 +138,13 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } @Override + public CharSequence getWorkProfileOrganizationName() { + final int profileId = getWorkProfileUserId(mCurrentUserId); + if (profileId == UserHandle.USER_NULL) return null; + return mDevicePolicyManager.getOrganizationNameForUser(profileId); + } + + @Override public String getPrimaryVpnName() { VpnConfig cfg = mCurrentVpns.get(mVpnUserId); if (cfg != null) { @@ -147,17 +154,28 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } } - @Override - public String getProfileVpnName() { - for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) { - if (profileId == mVpnUserId) { - continue; - } - VpnConfig cfg = mCurrentVpns.get(profileId); - if (cfg != null) { - return getNameForVpnConfig(cfg, UserHandle.of(profileId)); + private int getWorkProfileUserId(int userId) { + for (final UserInfo userInfo : mUserManager.getProfiles(userId)) { + if (userInfo.isManagedProfile()) { + return userInfo.id; } } + return UserHandle.USER_NULL; + } + + @Override + public boolean hasWorkProfile() { + return getWorkProfileUserId(mCurrentUserId) != UserHandle.USER_NULL; + } + + @Override + public String getWorkProfileVpnName() { + final int profileId = getWorkProfileUserId(mVpnUserId); + if (profileId == UserHandle.USER_NULL) return null; + VpnConfig cfg = mCurrentVpns.get(profileId); + if (cfg != null) { + return getNameForVpnConfig(cfg, UserHandle.of(profileId)); + } return null; } @@ -199,6 +217,18 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } @Override + public boolean hasCACertInCurrentUser() { + //TODO: implement + return false; + } + + @Override + public boolean hasCACertInWorkProfile() { + //TODO: implement + return false; + } + + @Override public void removeCallback(SecurityControllerCallback callback) { synchronized (mCallbacks) { if (callback == null) return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java index aaa0568a72cd..ec7e557fde81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java @@ -44,10 +44,6 @@ class TelephonyIcons { static final int ICON_4G_PLUS = R.drawable.stat_sys_data_fully_connected_4g_plus; static final int ICON_1X = R.drawable.stat_sys_data_fully_connected_1x; - static final int ICON_DATA_DISABLED = R.drawable.stat_sys_data_disabled; - - static final int QS_ICON_DATA_DISABLED = R.drawable.ic_qs_data_disabled; - static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup( "CARRIER_NETWORK_CHANGE", null, @@ -221,20 +217,5 @@ class TelephonyIcons { true, TelephonyIcons.QS_DATA_LTE_PLUS ); - - static final MobileIconGroup DATA_DISABLED = new MobileIconGroup( - "DataDisabled", - null, - null, - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, - 0, 0, - 0, - 0, - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], - R.string.accessibility_cell_data_off, - TelephonyIcons.ICON_DATA_DISABLED, - false, - TelephonyIcons.QS_ICON_DATA_DISABLED - ); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java index 374408d3ec7a..dfc3591678ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java @@ -47,6 +47,7 @@ public class WifiIcons { static final int QS_WIFI_NO_NETWORK = R.drawable.ic_qs_wifi_no_network; static final int WIFI_NO_NETWORK = R.drawable.stat_sys_wifi_signal_null; + static final int WIFI_DISCONNECTED = R.drawable.stat_sys_wifi_signal_disconnected; static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 2104cb1421aa..a773acf317c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -80,7 +80,7 @@ public class WifiSignalController extends AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH, WifiIcons.WIFI_NO_NETWORK, WifiIcons.QS_WIFI_NO_NETWORK, - WifiIcons.WIFI_NO_NETWORK, + WifiIcons.WIFI_DISCONNECTED, WifiIcons.QS_WIFI_NO_NETWORK, AccessibilityContentDescriptions.WIFI_NO_CONNECTION ); @@ -133,8 +133,7 @@ public class WifiSignalController extends @Override public void notifyListeners(SignalCallback callback) { // only show wifi in the cluster if connected or if wifi-only - boolean wifiVisible = mCurrentState.enabled - && (mCurrentState.connected || !mHasMobileData); + boolean wifiVisible = true; String wifiDesc = wifiVisible ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getStringIfExists(getContentDescription()); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 697cac99f244..b8b046b9990d 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -433,7 +433,7 @@ public class VolumeDialogImpl implements VolumeDialog, TunerService.Tunable { return false; } }); - row.icon = (ImageButton) row.view.findViewById(R.id.volume_row_icon); + row.icon = row.view.findViewById(R.id.volume_row_icon); row.icon.setImageResource(iconRes); if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) { row.icon.setOnClickListener(new OnClickListener() { @@ -465,6 +465,8 @@ public class VolumeDialogImpl implements VolumeDialog, TunerService.Tunable { row.userAttempt = 0; // reset the grace period, slider updates immediately } }); + } else { + row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 1ff373c95c14..ff644d8bb618 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -40,6 +40,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +/* + * Compile and run the whole SystemUI test suite: + runtest --path frameworks/base/packages/SystemUI/tests + * + * Compile and run just this class: + runtest --path \ + frameworks/base/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +*/ + @SmallTest @RunWith(AndroidJUnit4.class) public class QSSecurityFooterTest extends SysuiTestCase { @@ -47,11 +56,11 @@ public class QSSecurityFooterTest extends SysuiTestCase { private final String MANAGING_ORGANIZATION = "organization"; private final String DEVICE_OWNER_PACKAGE = "TestDPC"; private final String VPN_PACKAGE = "TestVPN"; + private final String VPN_PACKAGE_2 = "TestVPN 2"; private ViewGroup mRootView; private TextView mFooterText; private TestableImageView mFooterIcon; - private TestableImageView mFooterIcon2; private QSSecurityFooter mFooter; private SecurityController mSecurityController = mock(SecurityController.class); @@ -69,15 +78,12 @@ public class QSSecurityFooterTest extends SysuiTestCase { mRootView = (ViewGroup) mFooter.getView(); mFooterText = (TextView) mRootView.findViewById(R.id.footer_text); mFooterIcon = (TestableImageView) mRootView.findViewById(R.id.footer_icon); - mFooterIcon2 = (TestableImageView) mRootView.findViewById(R.id.footer_icon2); mFooter.setHostEnvironment(null); } @Test public void testUnmanaged() { when(mSecurityController.isDeviceManaged()).thenReturn(false); - when(mSecurityController.isVpnEnabled()).thenReturn(false); - when(mSecurityController.isVpnBranded()).thenReturn(false); mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); @@ -91,8 +97,12 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - assertEquals(mContext.getString(R.string.do_disclosure_generic), mFooterText.getText()); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_management), + mFooterText.getText()); assertEquals(View.VISIBLE, mRootView.getVisibility()); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); } @Test @@ -103,37 +113,100 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - assertEquals(mContext.getString(R.string.do_disclosure_with_name, MANAGING_ORGANIZATION), + assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management, + MANAGING_ORGANIZATION), mFooterText.getText()); assertEquals(View.VISIBLE, mRootView.getVisibility()); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); } @Test public void testNetworkLoggingEnabled() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); - when(mSecurityController.isVpnEnabled()).thenReturn(false); mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), + mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); - assertEquals(R.drawable.ic_qs_network_logging, mFooterIcon.getLastImageResource()); - assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); + + // Same situation, but with organization name set + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString( + R.string.quick_settings_disclosure_named_management_monitoring, + MANAGING_ORGANIZATION), + mFooterText.getText()); } @Test - public void testVpnEnabled() { + public void testManagedCACertsInstalled() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), + mFooterText.getText()); + } + + @Test + public void testManagedOneVpnEnabled() { when(mSecurityController.isDeviceManaged()).thenReturn(true); - when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(false); when(mSecurityController.isVpnEnabled()).thenReturn(true); - when(mSecurityController.isVpnBranded()).thenReturn(false); + when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_named_vpn, + VPN_PACKAGE), + mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); - // -1 == never set. - assertEquals(-1, mFooterIcon.getLastImageResource()); - assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility()); + assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource()); + + // Same situation, but with organization name set + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString( + R.string.quick_settings_disclosure_named_management_named_vpn, + MANAGING_ORGANIZATION, VPN_PACKAGE), + mFooterText.getText()); + } + + @Test + public void testManagedTwoVpnsEnabled() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.isVpnEnabled()).thenReturn(true); + when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); + when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns), + mFooterText.getText()); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource()); + + // Same situation, but with organization name set + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management_vpns, + MANAGING_ORGANIZATION), + mFooterText.getText()); } @Test @@ -141,15 +214,101 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); when(mSecurityController.isVpnEnabled()).thenReturn(true); - when(mSecurityController.isVpnBranded()).thenReturn(false); + when(mSecurityController.getPrimaryVpnName()).thenReturn("VPN Test App"); mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); - assertEquals(View.VISIBLE, mFooterIcon2.getVisibility()); + assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource()); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), + mFooterText.getText()); + } + + @Test + public void testWorkProfileCACertsInstalled() { + when(mSecurityController.isDeviceManaged()).thenReturn(false); + when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); - assertEquals(-1, mFooterIcon2.getLastImageResource()); + assertEquals(mContext.getString( + R.string.quick_settings_disclosure_managed_profile_monitoring), + mFooterText.getText()); + + // Same situation, but with organization name set + when(mSecurityController.getWorkProfileOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString( + R.string.quick_settings_disclosure_named_managed_profile_monitoring, + MANAGING_ORGANIZATION), + mFooterText.getText()); + } + + @Test + public void testCACertsInstalled() { + when(mSecurityController.isDeviceManaged()).thenReturn(false); + when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_monitoring), + mFooterText.getText()); + } + + @Test + public void testTwoVpnsEnabled() { + when(mSecurityController.isVpnEnabled()).thenReturn(true); + when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); + when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource()); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns), + mFooterText.getText()); + } + + @Test + public void testWorkProfileVpnEnabled() { + when(mSecurityController.isVpnEnabled()).thenReturn(true); + when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource()); + assertEquals(mContext.getString( + R.string.quick_settings_disclosure_managed_profile_named_vpn, + VPN_PACKAGE_2), + mFooterText.getText()); + } + + @Test + public void testVpnEnabled() { + when(mSecurityController.isVpnEnabled()).thenReturn(true); + when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource()); + assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn, + VPN_PACKAGE), + mFooterText.getText()); + + when(mSecurityController.hasWorkProfile()).thenReturn(true); + mFooter.refreshState(); + + waitForIdleSync(mFooter.mHandler); + assertEquals(mContext.getString( + R.string.quick_settings_disclosure_personal_profile_named_vpn, + VPN_PACKAGE), + mFooterText.getText()); } @Test @@ -160,8 +319,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { null /* primaryVpn */, null /* profileVpn */, null /* deviceOwnerOrganization */, - false /* hasProfileOwner */, - false /* isBranded */)); + false /* hasProfileOwner */)); } @Test @@ -172,8 +330,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { VPN_PACKAGE, null /* profileVpn */, null /* deviceOwnerOrganization */, - false /* hasProfileOwner */, - false /* isBranded */)); + false /* hasProfileOwner */)); } @Test @@ -184,8 +341,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { null /* primaryVpn */, null /* profileVpn */, MANAGING_ORGANIZATION, - false /* hasProfileOwner */, - false /* isBranded */)); + false /* hasProfileOwner */)); } @Test @@ -196,8 +352,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { VPN_PACKAGE, null /* profileVpn */, MANAGING_ORGANIZATION, - false /* hasProfileOwner */, - false /* isBranded */)); + false /* hasProfileOwner */)); } private CharSequence getExpectedMessage(boolean hasDeviceOwnerOrganization, boolean hasVPN) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java index 3ed1681644f2..f6c75a8f0b84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java @@ -114,33 +114,26 @@ public class CallbackHandlerTest { boolean wide = true; int subId = 5; boolean roaming = true; - mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription, - description, wide, subId, roaming); + boolean isEmergency = true; + mHandler.setMobileDataIndicators(status, type, in, out, typeDescription, + subId, roaming, isEmergency); waitForCallbacks(); ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<String> typeContentArg = ArgumentCaptor.forClass(String.class); - ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); - ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class); Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(), - qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(), - outArg.capture(), typeContentArg.capture(), descArg.capture(), wideArg.capture(), - subIdArg.capture(), eq(roaming)); + typeIconArg.capture(), inArg.capture(), + outArg.capture(), typeContentArg.capture(), + subIdArg.capture(), eq(roaming), eq(isEmergency)); assertEquals(status, statusArg.getValue()); - assertEquals(qs, qsArg.getValue()); assertEquals(type, (int) typeIconArg.getValue()); - assertEquals(qsType, (int) qsTypeIconArg.getValue()); assertEquals(in, (boolean) inArg.getValue()); assertEquals(out, (boolean) outArg.getValue()); assertEquals(typeDescription, typeContentArg.getValue()); - assertEquals(description, descArg.getValue()); - assertEquals(wide, (boolean) wideArg.getValue()); assertEquals(subId, (int) subIdArg.getValue()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 505e1d8346fd..b39171e9f720 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -29,6 +29,7 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; + import com.android.internal.telephony.cdma.EriInfo; import com.android.settingslib.net.DataUsageController; import com.android.systemui.statusbar.phone.SignalDrawable; @@ -45,8 +46,6 @@ import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import java.io.PrintWriter; import java.io.StringWriter; @@ -70,7 +69,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected static final int DEFAULT_SIGNAL_STRENGTH = DEFAULT_LEVEL; protected static final int DEFAULT_QS_SIGNAL_STRENGTH = DEFAULT_LEVEL; protected static final int DEFAULT_ICON = TelephonyIcons.ICON_3G; - protected static final int DEFAULT_QS_ICON = TelephonyIcons.QS_DATA_3G; + protected static final int DEFAULT_QS_ICON = DEFAULT_ICON; protected NetworkControllerImpl mNetworkController; protected MobileSignalController mMobileSignalController; @@ -117,7 +116,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true); when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn( - new NetworkCapabilities[] { mNetCapabilities }); + new NetworkCapabilities[]{mNetCapabilities}); mSignalStrength = mock(SignalStrength.class); mServiceState = mock(ServiceState.class); @@ -175,17 +174,17 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } protected NetworkControllerImpl setUpNoMobileData() { - when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false); - NetworkControllerImpl networkControllerNoMobile - = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager, mMockTm, - mMockWm, mMockSm, mConfig, mContext.getMainLooper(), mCallbackHandler, - mock(AccessPointControllerImpl.class), - mock(DataUsageController.class), mMockSubDefaults, - mock(DeviceProvisionedController.class)); + when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false); + NetworkControllerImpl networkControllerNoMobile + = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager, mMockTm, + mMockWm, mMockSm, mConfig, mContext.getMainLooper(), mCallbackHandler, + mock(AccessPointControllerImpl.class), + mock(DataUsageController.class), mMockSubDefaults, + mock(DeviceProvisionedController.class)); - setupNetworkController(); + setupNetworkController(); - return networkControllerNoMobile; + return networkControllerNoMobile; } @@ -308,11 +307,10 @@ public class NetworkControllerBaseTest extends SysuiTestCase { ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( - any(), - iconArg.capture(), - anyInt(), - typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), - anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean()); + iconArg.capture(), + typeIconArg.capture(), + dataInArg.capture(), dataOutArg.capture(), + anyString(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); int state = SignalDrawable.getState(icon, SignalStrength.NUM_SIGNAL_STRENGTH_BINS, false); @@ -335,17 +333,16 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon, - boolean roaming, boolean inet) { + boolean roaming, boolean inet) { ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); // TODO: Verify all fields. Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( iconArg.capture(), - any(), typeIconArg.capture(), - anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(), - anyInt(), eq(roaming)); + anyBoolean(), anyBoolean(), anyString(), + anyInt(), eq(roaming), anyBoolean()); IconState iconState = iconArg.getValue(); int state = icon == -1 ? 0 @@ -356,22 +353,18 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon, - boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut) { + boolean qsVisible, boolean dataIn, boolean dataOut) { ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( iconArg.capture(), - qsIconArg.capture(), typeIconArg.capture(), - qsTypeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), - anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); @@ -381,17 +374,15 @@ public class NetworkControllerBaseTest extends SysuiTestCase { assertEquals("Signal icon in status bar", state, iconState.icon); assertEquals("Visibility in status bar", visible, iconState.visible); - iconState = qsIconArg.getValue(); assertEquals("Visibility in quick settings", qsVisible, iconState.visible); assertEquals("Signal icon in quick settings", state, iconState.icon); - assertEquals("Data icon in quick settings", qsTypeIcon, (int) qsTypeIconArg.getValue()); assertEquals("Data direction in in quick settings", dataIn, (boolean) dataInArg.getValue()); assertEquals("Data direction out in quick settings", dataOut, (boolean) dataOutArg.getValue()); } - protected void assertNetworkNameEquals(String expected) { - assertEquals("Network name", expected, mMobileSignalController.getState().networkName); - } + protected void assertNetworkNameEquals(String expected) { + assertEquals("Network name", expected, mMobileSignalController.getState().networkName); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index dfe00f95fe4d..6470c117f406 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -1,5 +1,9 @@ package com.android.systemui.statusbar.policy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -11,10 +15,14 @@ import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import com.android.settingslib.net.DataUsageController; +import com.android.systemui.statusbar.phone.SignalDrawable; +import com.android.systemui.statusbar.policy.NetworkController.IconState; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; @SmallTest @RunWith(AndroidJUnit4.class) @@ -116,8 +124,11 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); - verifyDataIndicators(TelephonyIcons.ICON_DATA_DISABLED, - TelephonyIcons.QS_ICON_DATA_DISABLED); + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( + iconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(), anyInt(), + anyBoolean(), anyBoolean()); + assertEquals(SignalDrawable.STATE_CUT, SignalDrawable.getState(iconArg.getValue().icon)); } @Test @@ -129,9 +140,14 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(false); mUserCallback.onUserSetupChanged(); + waitForIdleSync(); // Don't show the X until the device is setup. - verifyDataIndicators(0, 0); + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( + iconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(), anyInt(), + anyBoolean(), anyBoolean()); + assertNotEquals(SignalDrawable.STATE_CUT, SignalDrawable.getState(iconArg.getValue().icon)); } @Test @@ -181,12 +197,12 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { updateDataActivity(direction); verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, DEFAULT_ICON, true, - DEFAULT_QS_SIGNAL_STRENGTH, DEFAULT_QS_ICON, in, out); + in, out); } private void verifyDataIndicators(int dataIcon, int qsDataIcon) { verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, dataIcon, - true, DEFAULT_QS_SIGNAL_STRENGTH, qsDataIcon, false, + true, false, false); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index 1627925ae1bc..e542c371feda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -214,7 +214,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { verifyLastQsMobileDataIndicators(true, testStrength, - TelephonyIcons.QS_DATA_1X, false, false); + TelephonyIcons.ICON_1X, false, false); } } @@ -434,7 +434,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { verifyLastQsMobileDataIndicators(true /* visible */, DEFAULT_LEVEL /* icon */, - DEFAULT_QS_ICON /* typeIcon */, + DEFAULT_ICON /* typeIcon */, false /* dataIn */, true /* dataOut */); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 73fa5aa180bf..edfa3261a7e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -62,7 +62,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { public void testWifiIcon() { String testSsid = "Test SSID"; setWifiEnabled(true); - verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK); + verifyLastWifiIcon(true, WifiIcons.WIFI_DISCONNECTED); setWifiState(true, testSsid); verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java index 157b8a04720b..fee5e32398c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java @@ -36,6 +36,11 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override + public boolean hasWorkProfile() { + return false; + } + + @Override public String getDeviceOwnerName() { return null; } @@ -51,6 +56,11 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override + public CharSequence getWorkProfileOrganizationName() { + return null; + } + + @Override public boolean isNetworkLoggingEnabled() { return false; } @@ -76,11 +86,21 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override - public String getProfileVpnName() { + public String getWorkProfileVpnName() { return null; } @Override + public boolean hasCACertInCurrentUser() { + return false; + } + + @Override + public boolean hasCACertInWorkProfile() { + return false; + } + + @Override public void onUserSwitched(int newUserId) { } diff --git a/services/Android.mk b/services/Android.mk index a4c891b46011..5c863b02c5a4 100644 --- a/services/Android.mk +++ b/services/Android.mk @@ -27,6 +27,7 @@ services := \ appwidget \ autofill \ backup \ + companion \ coverage\ devicepolicy \ midi \ diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index d6f525655d56..1b5b2c65e5b2 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -48,6 +48,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; +import android.service.autofill.FillEventHistory; import android.util.LocalLog; import android.util.Log; import android.util.Slog; @@ -184,7 +185,7 @@ public final class AutofillManagerService extends SystemService { } @Override - public void onStopUser(int userId) { + public void onCleanupUser(int userId) { synchronized (mLock) { removeCachedServiceLocked(userId); } @@ -211,7 +212,7 @@ public final class AutofillManagerService extends SystemService { /** * Peeks the service instance for a user. * - * @return service instance or null if not already present + * @return service instance or {@code null} if not already present */ @Nullable AutofillManagerServiceImpl peekServiceForUserLocked(int userId) { @@ -398,6 +399,21 @@ public final class AutofillManagerService extends SystemService { } @Override + public FillEventHistory getFillEventHistory() throws RemoteException { + UserHandle user = getCallingUserHandle(); + int uid = getCallingUid(); + + synchronized (mLock) { + AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier()); + if (service != null) { + return service.getFillEventHistory(uid); + } + } + + return null; + } + + @Override public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback) throws RemoteException { activityToken = Preconditions.checkNotNull(activityToken, "activityToken"); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 57ef6d01a96b..e274e18e1b31 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -48,7 +48,9 @@ import android.os.UserManager; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; +import android.service.autofill.FillEventHistory; import android.service.autofill.FillRequest; +import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; import android.text.TextUtils; import android.util.LocalLog; @@ -122,6 +124,10 @@ final class AutofillManagerServiceImpl { @GuardedBy("mLock") private final SparseArray<Session> mSessions = new SparseArray<>(); + /** The last selection */ + @GuardedBy("mLock") + private FillEventHistory mEventHistory; + /** * Receiver of assist data from the app's {@link Activity}. */ @@ -500,6 +506,72 @@ final class AutofillManagerServiceImpl { return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()); } + /** + * Initializes the last fill selection after an autofill service returned a new + * {@link FillResponse}. + */ + void setLastResponse(int serviceUid, @NonNull FillResponse response) { + synchronized (mLock) { + mEventHistory = new FillEventHistory(serviceUid, response.getClientState()); + } + } + + /** + * Updates the last fill selection when an authentication was selected. + */ + void setAuthenticationSelected() { + synchronized (mLock) { + mEventHistory.addEvent( + new FillEventHistory.Event(FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED, null)); + } + } + + /** + * Updates the last fill selection when an dataset authentication was selected. + */ + void setDatasetAuthenticationSelected(@Nullable String selectedDataset) { + synchronized (mLock) { + mEventHistory.addEvent(new FillEventHistory.Event( + FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset)); + } + } + + /** + * Updates the last fill selection when an save Ui is shown. + */ + void setSaveShown() { + synchronized (mLock) { + mEventHistory.addEvent(new FillEventHistory.Event(FillEventHistory.Event.TYPE_SAVE_SHOWN, null)); + } + } + + /** + * Updates the last fill response when a dataset was selected. + */ + void setDatasetSelected(@Nullable String selectedDataset) { + synchronized (mLock) { + mEventHistory.addEvent( + new FillEventHistory.Event(FillEventHistory.Event.TYPE_DATASET_SELECTED, selectedDataset)); + } + } + + /** + * Gets the fill event history. + * + * @param callingUid The calling uid + * + * @return The history or {@code null} if there is none. + */ + FillEventHistory getFillEventHistory(int callingUid) { + synchronized (mLock) { + if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) { + return mEventHistory; + } + } + + return null; + } + void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; @@ -528,6 +600,20 @@ final class AutofillManagerServiceImpl { mSessions.valueAt(i).dumpLocked(prefix2, pw); } } + + if (mEventHistory == null || mEventHistory.getEvents().size() == 0) { + pw.print(prefix); pw.println("No event on last fill response"); + } else { + pw.print(prefix); pw.println("Events of last fill response:"); + pw.print(prefix); + + int numEvents = mEventHistory.getEvents().size(); + for (int i = 0; i < numEvents; i++) { + FillEventHistory.Event event = mEventHistory.getEvents().get(i); + pw.println(" " + i + ": eventType=" + event.getType() + " datasetId=" + + event.getDatasetId()); + } + } } void destroySessionsLocked() { diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index 3badcfcb61cd..4d0f38042276 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -87,7 +87,7 @@ final class RemoteFillService implements DeathRecipient { private PendingRequest mPendingRequest; public interface FillServiceCallbacks { - void onFillRequestSuccess(@Nullable FillResponse response, + void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid, @NonNull String servicePackageName, int requestId); void onFillRequestFailure(@Nullable CharSequence message, @NonNull String servicePackageName); @@ -252,11 +252,11 @@ final class RemoteFillService implements DeathRecipient { } private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest, - FillResponse response, int requestId) { + int callingUid, FillResponse response, int requestId) { mHandler.getHandler().post(() -> { if (handleResponseCallbackCommon(pendingRequest)) { - mCallbacks.onFillRequestSuccess(response, mComponentName.getPackageName(), - requestId); + mCallbacks.onFillRequestSuccess(response, callingUid, + mComponentName.getPackageName(), requestId); } }); } @@ -424,7 +424,7 @@ final class RemoteFillService implements DeathRecipient { RemoteFillService remoteService = mWeakService.get(); if (remoteService != null) { remoteService.dispatchOnFillRequestSuccess( - PendingFillRequest.this, response, requestId); + PendingFillRequest.this, getCallingUid(), response, requestId); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 5feb81db4a0b..2b9961428a29 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -68,6 +68,7 @@ import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.Map; import java.util.Map.Entry; @@ -220,12 +221,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mActivityToken = newActivity; mClient = IAutoFillManagerClient.Stub.asInterface(newClient); + + // The tracked id are not persisted in the client, hence update them + updateTrackedIdsLocked(); } } // FillServiceCallbacks @Override - public void onFillRequestSuccess(@Nullable FillResponse response, + public void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid, @NonNull String servicePackageName, int requestId) { if (response == null) { if ((mFlags & FLAG_MANUAL_REQUEST) != 0) { @@ -237,6 +241,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + mService.setLastResponse(serviceUid, response); + if ((response.getDatasets() == null || response.getDatasets().isEmpty()) && response.getAuthentication() == null) { // Response is "empty" from an UI point of view, need to notify client. @@ -310,6 +316,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { fillInIntent = createAuthFillInIntent(mStructure, extras); } + + mService.setAuthenticationSelected(); + mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent)); } @@ -438,7 +447,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ public boolean showSaveLocked() { if (mStructure == null) { - Slog.wtf(TAG, "showSaveLocked(): no mStructure"); + Slog.d(TAG, "showSaveLocked(): no mStructure"); return true; } if (mResponses == null) { @@ -538,6 +547,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } if (atLeastOneChanged) { + mService.setSaveShown(); getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName); return false; } @@ -749,6 +759,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + private void updateTrackedIdsLocked() { + if (mResponses == null || mResponses.size() == 0) { + return; + } + + // Only track the views of the last response as only those are reported back to the + // service, see #showSaveLocked + ArrayList<AutofillId> trackedViews = new ArrayList<>(); + boolean saveOnAllViewsInvisible = false; + SaveInfo saveInfo = mResponses.valueAt(getLastResponseIndex()).getSaveInfo(); + if (saveInfo != null) { + saveOnAllViewsInvisible = + (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0; + + // We only need to track views if we want to save once they become invisible. + if (saveOnAllViewsInvisible) { + if (saveInfo.getRequiredIds() != null) { + Collections.addAll(trackedViews, saveInfo.getRequiredIds()); + } + + if (saveInfo.getOptionalIds() != null) { + Collections.addAll(trackedViews, saveInfo.getOptionalIds()); + } + } + } + + try { + mClient.setTrackedViews(id, trackedViews, saveOnAllViewsInvisible); + } catch (RemoteException e) { + Slog.w(TAG, "Cannot set tracked ids", e); + } + } + private void processResponseLocked(FillResponse response, int requestId) { if (DEBUG) { Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response); @@ -763,6 +806,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } setViewStatesLocked(response, ViewState.STATE_FILLABLE); + updateTrackedIdsLocked(); if (mCurrentViewId == null) { return; @@ -844,12 +888,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { // Autofill it directly... if (dataset.getAuthentication() == null) { + mService.setDatasetSelected(dataset.getId()); + autoFillApp(dataset); return; } // ...or handle authentication. // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already + mService.setDatasetAuthenticationSelected(dataset.getId()); mDatasetWaitingAuth = dataset; setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH); final Intent fillInIntent = createAuthFillInIntent(mStructure, null); diff --git a/services/companion/Android.mk b/services/companion/Android.mk new file mode 100644 index 000000000000..be48761cbae5 --- /dev/null +++ b/services/companion/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := services.companion + +LOCAL_SRC_FILES += \ + $(call all-java-files-under,java) + +LOCAL_JAVA_LIBRARIES := services.core + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 122a954b4d30..41b70a1312e8 100644 --- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -15,7 +15,7 @@ */ -package com.android.server.print; +package com.android.server.companion; import static com.android.internal.util.CollectionUtils.size; import static com.android.internal.util.Preconditions.checkArgument; @@ -81,7 +81,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; -//TODO move to own package! //TODO onStop schedule unbind in 5 seconds //TODO make sure APIs are only callable from currently focused app //TODO schedule stopScan on activity destroy(except if configuration change) diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 2e61550614f2..6502c012ef5b 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -25,7 +25,7 @@ option java_package com.android.server # This is logged when the screen on broadcast has completed 2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1) # This is logged when the screen is turned on or off. -2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1) +2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1),(latency|1|3) # This is logged when the partial wake lock (keeping the device awake # regardless of whether the screen is off) is acquired or released. 2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3) diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 39bfedae69d8..8ad3d23648bf 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.annotation.BinderThread; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -2146,6 +2147,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); } + @BinderThread @SuppressWarnings("deprecation") @Override public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis, @@ -2161,9 +2163,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mBackDisposition = backDisposition; updateSystemUiLocked(token, vis, backDisposition); } + + final boolean dismissImeOnBackKeyPressed; + switch (backDisposition) { + case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: + dismissImeOnBackKeyPressed = true; + break; + case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: + dismissImeOnBackKeyPressed = false; + break; + default: + case InputMethodService.BACK_DISPOSITION_DEFAULT: + dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0); + break; + } mWindowManagerInternal.updateInputMethodWindowStatus(token, (vis & InputMethodService.IME_VISIBLE) != 0, - info != null ? info.mTargetWindow : null); + dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null); } private void updateSystemUi(IBinder token, int vis, int backDisposition) { diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java index 8ae95d5ada22..bb530fb8c13f 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/services/core/java/com/android/server/NsdService.java @@ -57,46 +57,27 @@ public class NsdService extends INsdManager.Stub { private static final String TAG = "NsdService"; private static final String MDNS_TAG = "mDnsConnector"; - private static final boolean DBG = false; + private static final boolean DBG = true; - private Context mContext; - private ContentResolver mContentResolver; - private NsdStateMachine mNsdStateMachine; + private final Context mContext; + private final ContentResolver mContentResolver; + private final NsdStateMachine mNsdStateMachine; + private final NativeDaemonConnector mNativeConnector; + private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1); /** * Clients receiving asynchronous messages */ - private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(); + private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>(); /* A map from unique id to client info */ - private SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<ClientInfo>(); + private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); - private AsyncChannel mReplyChannel = new AsyncChannel(); + private final AsyncChannel mReplyChannel = new AsyncChannel(); - private int INVALID_ID = 0; + private static final int INVALID_ID = 0; private int mUniqueId = 1; - private static final int BASE = Protocol.BASE_NSD_MANAGER; - private static final int CMD_TO_STRING_COUNT = NsdManager.RESOLVE_SERVICE - BASE + 1; - private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; - - static { - sCmdToString[NsdManager.DISCOVER_SERVICES - BASE] = "DISCOVER"; - sCmdToString[NsdManager.STOP_DISCOVERY - BASE] = "STOP-DISCOVER"; - sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER"; - sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER"; - sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE"; - } - - private static String cmdToString(int cmd) { - cmd -= BASE; - if ((cmd >= 0) && (cmd < sCmdToString.length)) { - return sCmdToString[cmd]; - } else { - return null; - } - } - private class NsdStateMachine extends StateMachine { private final DefaultState mDefaultState = new DefaultState(); @@ -105,7 +86,7 @@ public class NsdService extends INsdManager.Stub { @Override protected String getWhatToString(int what) { - return cmdToString(what); + return NsdManager.nameOf(what); } /** @@ -114,13 +95,13 @@ public class NsdService extends INsdManager.Stub { private void registerForNsdSetting() { ContentObserver contentObserver = new ContentObserver(this.getHandler()) { @Override - public void onChange(boolean selfChange) { - if (isNsdEnabled()) { - mNsdStateMachine.sendMessage(NsdManager.ENABLE); - } else { - mNsdStateMachine.sendMessage(NsdManager.DISABLE); - } + public void onChange(boolean selfChange) { + if (isNsdEnabled()) { + mNsdStateMachine.sendMessage(NsdManager.ENABLE); + } else { + mNsdStateMachine.sendMessage(NsdManager.DISABLE); } + } }; mContext.getContentResolver().registerContentObserver( @@ -272,20 +253,17 @@ public class NsdService extends INsdManager.Stub { public boolean processMessage(Message msg) { ClientInfo clientInfo; NsdServiceInfo servInfo; - boolean result = HANDLED; int id; switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: //First client if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL && mClients.size() == 0) { startMDnsDaemon(); } - result = NOT_HANDLED; - break; + return NOT_HANDLED; case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - result = NOT_HANDLED; - break; + return NOT_HANDLED; case NsdManager.DISABLE: //TODO: cleanup clients transitionTo(mDisabledState); @@ -396,25 +374,23 @@ public class NsdService extends INsdManager.Stub { case NsdManager.NATIVE_DAEMON_EVENT: NativeEvent event = (NativeEvent) msg.obj; if (!handleNativeEvent(event.code, event.raw, event.cooked)) { - result = NOT_HANDLED; + return NOT_HANDLED; } break; default: - result = NOT_HANDLED; - break; + return NOT_HANDLED; } - return result; + return HANDLED; } private boolean handleNativeEvent(int code, String raw, String[] cooked) { - boolean handled = true; NsdServiceInfo servInfo; int id = Integer.parseInt(cooked[1]); ClientInfo clientInfo = mIdToClientInfoMap.get(id); if (clientInfo == null) { - Slog.e(TAG, "Unique id with no client mapping: " + id); - handled = false; - return handled; + String name = NativeResponseCode.nameOf(code); + Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name)); + return false; } /* This goes in response as msg.arg2 */ @@ -423,42 +399,42 @@ public class NsdService extends INsdManager.Stub { // This can happen because of race conditions. For example, // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY, // and we may get in this situation. - Slog.d(TAG, "Notification for a listener that is no longer active: " + id); - handled = false; - return handled; + String name = NativeResponseCode.nameOf(code); + Slog.d(TAG, String.format( + "Notification %s for listener id %d that is no longer active", + name, id)); + return false; + } + if (DBG) { + String name = NativeResponseCode.nameOf(code); + Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw)); } - switch (code) { case NativeResponseCode.SERVICE_FOUND: /* NNN uniqueId serviceName regType domain */ - if (DBG) Slog.d(TAG, "SERVICE_FOUND Raw: " + raw); servInfo = new NsdServiceInfo(cooked[2], cooked[3]); clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0, clientId, servInfo); break; case NativeResponseCode.SERVICE_LOST: /* NNN uniqueId serviceName regType domain */ - if (DBG) Slog.d(TAG, "SERVICE_LOST Raw: " + raw); servInfo = new NsdServiceInfo(cooked[2], cooked[3]); clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0, clientId, servInfo); break; case NativeResponseCode.SERVICE_DISCOVERY_FAILED: /* NNN uniqueId errorCode */ - if (DBG) Slog.d(TAG, "SERVICE_DISC_FAILED Raw: " + raw); clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, NsdManager.FAILURE_INTERNAL_ERROR, clientId); break; case NativeResponseCode.SERVICE_REGISTERED: /* NNN regId serviceName regType */ - if (DBG) Slog.d(TAG, "SERVICE_REGISTERED Raw: " + raw); servInfo = new NsdServiceInfo(cooked[2], null); clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, id, clientId, servInfo); break; case NativeResponseCode.SERVICE_REGISTRATION_FAILED: /* NNN regId errorCode */ - if (DBG) Slog.d(TAG, "SERVICE_REGISTER_FAILED Raw: " + raw); clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, NsdManager.FAILURE_INTERNAL_ERROR, clientId); break; @@ -470,7 +446,6 @@ public class NsdService extends INsdManager.Stub { break; case NativeResponseCode.SERVICE_RESOLVED: /* NNN resolveId fullName hostName port txtlen txtdata */ - if (DBG) Slog.d(TAG, "SERVICE_RESOLVED Raw: " + raw); int index = 0; while (index < cooked[2].length() && cooked[2].charAt(index) != '.') { if (cooked[2].charAt(index) == '\\') { @@ -507,7 +482,6 @@ public class NsdService extends INsdManager.Stub { break; case NativeResponseCode.SERVICE_RESOLUTION_FAILED: /* NNN resolveId errorCode */ - if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); stopResolveService(id); removeRequestMap(clientId, id, clientInfo); clientInfo.mResolvedService = null; @@ -519,13 +493,11 @@ public class NsdService extends INsdManager.Stub { stopGetAddrInfo(id); removeRequestMap(clientId, id, clientInfo); clientInfo.mResolvedService = null; - if (DBG) Slog.d(TAG, "SERVICE_RESOLVE_FAILED Raw: " + raw); clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, NsdManager.FAILURE_INTERNAL_ERROR, clientId); break; case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: /* NNN resolveId hostname ttl addr */ - if (DBG) Slog.d(TAG, "SERVICE_GET_ADDR_SUCCESS Raw: " + raw); try { clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, @@ -539,10 +511,9 @@ public class NsdService extends INsdManager.Stub { clientInfo.mResolvedService = null; break; default: - handled = false; - break; + return false; } - return handled; + return true; } } } @@ -571,9 +542,6 @@ public class NsdService extends INsdManager.Stub { return sb.toString(); } - private NativeDaemonConnector mNativeConnector; - private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1); - private NsdService(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -634,7 +602,7 @@ public class NsdService extends INsdManager.Stub { } /* These should be in sync with system/netd/server/ResponseCode.h */ - class NativeResponseCode { + static final class NativeResponseCode { public static final int SERVICE_DISCOVERY_FAILED = 602; public static final int SERVICE_FOUND = 603; public static final int SERVICE_LOST = 604; @@ -650,6 +618,29 @@ public class NsdService extends INsdManager.Stub { public static final int SERVICE_GET_ADDR_FAILED = 611; public static final int SERVICE_GET_ADDR_SUCCESS = 612; + + private static final SparseArray<String> CODE_NAMES = new SparseArray<>(); + static { + CODE_NAMES.put(SERVICE_DISCOVERY_FAILED, "SERVICE_DISCOVERY_FAILED"); + CODE_NAMES.put(SERVICE_FOUND, "SERVICE_FOUND"); + CODE_NAMES.put(SERVICE_LOST, "SERVICE_LOST"); + CODE_NAMES.put(SERVICE_REGISTRATION_FAILED, "SERVICE_REGISTRATION_FAILED"); + CODE_NAMES.put(SERVICE_REGISTERED, "SERVICE_REGISTERED"); + CODE_NAMES.put(SERVICE_RESOLUTION_FAILED, "SERVICE_RESOLUTION_FAILED"); + CODE_NAMES.put(SERVICE_RESOLVED, "SERVICE_RESOLVED"); + CODE_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED"); + CODE_NAMES.put(SERVICE_UPDATE_FAILED, "SERVICE_UPDATE_FAILED"); + CODE_NAMES.put(SERVICE_GET_ADDR_FAILED, "SERVICE_GET_ADDR_FAILED"); + CODE_NAMES.put(SERVICE_GET_ADDR_SUCCESS, "SERVICE_GET_ADDR_SUCCESS"); + } + + static String nameOf(int code) { + String name = CODE_NAMES.get(code); + if (name == null) { + return Integer.toString(code); + } + return name; + } } private class NativeEvent { @@ -863,10 +854,10 @@ public class NsdService extends INsdManager.Stub { private NsdServiceInfo mResolvedService; /* A map from client id to unique id sent to mDns */ - private SparseArray<Integer> mClientIds = new SparseArray<Integer>(); + private final SparseArray<Integer> mClientIds = new SparseArray<>(); /* A map from client id to the type of the request we had received */ - private SparseArray<Integer> mClientRequests = new SparseArray<Integer>(); + private final SparseArray<Integer> mClientRequests = new SparseArray<>(); private ClientInfo(AsyncChannel c, Messenger m) { mChannel = c; diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index d79609856c3f..452fe1d70ddc 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3272,6 +3272,8 @@ class StorageManagerService extends IStorageManager.Stub try { return mContext.getSystemService(StorageStatsManager.class) .queryStatsForUid(volumeUuid, uid).getCacheBytes(); + } catch (IOException e) { + throw new ParcelableException(e); } finally { Binder.restoreCallingIdentity(token); } @@ -3312,6 +3314,8 @@ class StorageManagerService extends IStorageManager.Stub return Math.max(0, path.getUsableSpace() - storage.getStorageLowBytes(path)); } } + } catch (IOException e) { + throw new ParcelableException(e); } finally { Binder.restoreCallingIdentity(token); } @@ -3329,13 +3333,13 @@ class StorageManagerService extends IStorageManager.Stub + " because only " + allocatableBytes + " allocatable")); } - // Free up enough disk space to satisfy both the requested allocation - // and our low disk warning space. - final File path = storage.findPathForUuid(volumeUuid); - bytes += storage.getStorageLowBytes(path); - final long token = Binder.clearCallingIdentity(); try { + // Free up enough disk space to satisfy both the requested allocation + // and our low disk warning space. + final File path = storage.findPathForUuid(volumeUuid); + bytes += storage.getStorageLowBytes(path); + mPms.freeStorage(volumeUuid, bytes, flags); } catch (IOException e) { throw new ParcelableException(e); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c77820b4de3a..b91ecf5056ac 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -312,8 +312,7 @@ public final class ActiveServices { } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int id, Notification notification, int callingPid, int callingUid, - boolean fgRequired, String callingPackage, final int userId) + int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); @@ -464,10 +463,6 @@ public final class ActiveServices { } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - // STOPSHIP deprecated; remove when NotificationManager.startServiceInForeground is retired - if (notification != null) { - setServiceForegroundInnerLocked(r, id, notification, 0); - } return cmp; } @@ -761,7 +756,7 @@ public final class ActiveServices { } } if (r.fgRequired) { - if (DEBUG_BACKGROUND_CHECK) { + if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "Service called startForeground() as required: " + r); } r.fgRequired = false; @@ -1339,16 +1334,19 @@ public final class ActiveServices { final ComponentName comp = service.getComponent(); if (comp != null) { r = smap.mServicesByName.get(comp); + if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by component: " + r); } if (r == null && !isBindExternal) { Intent.FilterComparison filter = new Intent.FilterComparison(service); r = smap.mServicesByIntent.get(filter); + if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r); } if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0 && !callingPackage.equals(r.packageName)) { // If an external service is running within its own package, other packages // should not bind to that instance. r = null; + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service"); } if (r == null) { try { @@ -1408,12 +1406,14 @@ public final class ActiveServices { sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId); } r = smap.mServicesByName.get(name); + if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, + "Retrieved via pm by intent: " + r); if (r == null && createIfNeeded) { - Intent.FilterComparison filter + final Intent.FilterComparison filter = new Intent.FilterComparison(service.cloneFilter()); - ServiceRestarter res = new ServiceRestarter(); - BatteryStatsImpl.Uid.Pkg.Serv ss = null; - BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics(); + final ServiceRestarter res = new ServiceRestarter(); + final BatteryStatsImpl.Uid.Pkg.Serv ss; + final BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics(); synchronized (stats) { ss = stats.getServiceStatsLocked( sInfo.applicationInfo.uid, sInfo.packageName, @@ -1426,12 +1426,14 @@ public final class ActiveServices { // Make sure this component isn't in the pending list. for (int i=mPendingServices.size()-1; i>=0; i--) { - ServiceRecord pr = mPendingServices.get(i); + final ServiceRecord pr = mPendingServices.get(i); if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid && pr.name.equals(name)) { + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Remove pending: " + pr); mPendingServices.remove(i); } } + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Retrieve created new service: " + r); } } catch (RemoteException ex) { // pm is in same process, this will never happen. @@ -2119,7 +2121,28 @@ public final class ActiveServices { } } - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down " + r + " " + r.intent); + // Check to see if the service had been started as foreground, but being + // brought down before actually showing a notification. That is not allowed. + if (r.fgRequired) { + Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: " + + r); + r.fgRequired = false; + r.fgWaiting = false; + mAm.mHandler.removeMessages( + ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r); + if (r.app != null) { + Message msg = mAm.mHandler.obtainMessage( + ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG); + msg.obj = r.app; + mAm.mHandler.sendMessage(msg); + } + } + + if (DEBUG_SERVICE) { + RuntimeException here = new RuntimeException(); + here.fillInStackTrace(); + Slog.v(TAG_SERVICE, "Bringing down " + r + " " + r.intent, here); + } r.destroyTime = SystemClock.uptimeMillis(); if (LOG_SERVICE_START_STOP) { EventLogTags.writeAmDestroyService( @@ -2127,7 +2150,14 @@ public final class ActiveServices { } final ServiceMap smap = getServiceMapLocked(r.userId); - smap.mServicesByName.remove(r.name); + ServiceRecord found = smap.mServicesByName.remove(r.name); + if (found != r) { + // This is not actually the service we think is running... this should not happen, + // but if it does, fail hard. + smap.mServicesByName.put(r.name, found); + throw new IllegalStateException("Bringing down " + r + " but actually running " + + found); + } smap.mServicesByIntent.remove(r.intent); r.totalRestartCount = 0; unscheduleServiceRestartLocked(r, 0, true); @@ -2967,7 +2997,7 @@ public final class ActiveServices { void serviceForegroundTimeout(ServiceRecord r) { ProcessRecord app; synchronized (mAm) { - if (!r.fgRequired) { + if (!r.fgRequired || r.destroying) { return; } @@ -2985,6 +3015,11 @@ public final class ActiveServices { } } + void serviceForegroundCrash(ProcessRecord app) { + mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId, + "Context.startForegroundService() did not then call Service.startForeground()"); + } + void scheduleServiceTimeoutLocked(ProcessRecord proc) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6b0a73b25e71..52fc611f2988 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1230,6 +1230,20 @@ public class ActivityManagerService extends IActivityManager.Stub */ int[] mDeviceIdleTempWhitelist = new int[0]; + static final class PendingTempWhitelist { + final int targetUid; + final long duration; + final String tag; + + PendingTempWhitelist(int _targetUid, long _duration, String _tag) { + targetUid = _targetUid; + duration = _duration; + tag = _tag; + } + } + + final SparseArray<PendingTempWhitelist> mPendingTempWhitelist = new SparseArray<>(); + /** * Information about and control over application operations */ @@ -1688,6 +1702,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final int NOTIFY_VR_SLEEPING_MSG = 65; static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66; static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67; + static final int PUSH_TEMP_WHITELIST_UI_MSG = 68; + static final int SERVICE_FOREGROUND_CRASH_MSG = 69; static final int START_USER_SWITCH_FG_MSG = 712; static final int FIRST_ACTIVITY_STACK_MSG = 100; @@ -1921,6 +1937,9 @@ public class ActivityManagerService extends IActivityManager.Stub case DISPATCH_UIDS_CHANGED_UI_MSG: { dispatchUidsChanged(); } break; + case PUSH_TEMP_WHITELIST_UI_MSG: { + pushTempWhitelist(); + } break; } } } @@ -1956,6 +1975,9 @@ public class ActivityManagerService extends IActivityManager.Stub case SERVICE_FOREGROUND_TIMEOUT_MSG: { mServices.serviceForegroundTimeout((ServiceRecord)msg.obj); } break; + case SERVICE_FOREGROUND_CRASH_MSG: { + mServices.serviceForegroundCrash((ProcessRecord)msg.obj); + } break; case DISPATCH_PENDING_INTENT_CANCEL_MSG: { RemoteCallbackList<IResultReceiver> callbacks = (RemoteCallbackList<IResultReceiver>)msg.obj; @@ -6493,7 +6515,8 @@ public class ActivityManagerService extends IActivityManager.Stub // This is the first appearance of the uid, report it now! if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Creating new process uid: " + uidRec); - if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0) { + if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0 + || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) { uidRec.setWhitelist = uidRec.curWhitelist = true; } uidRec.updateHasInternetPermission(); @@ -7487,43 +7510,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - /** - * Whitelists {@code targetUid} to temporarily bypass Power Save mode. - */ - void tempWhitelistAppForPowerSave(int callerPid, int callerUid, int targetUid, long duration) { - if (DEBUG_WHITELISTS) { - Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", " - + targetUid + ", " + duration + ")"); - } - - if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid) - != PackageManager.PERMISSION_GRANTED) { - synchronized (mPidsSelfLocked) { - final ProcessRecord pr = mPidsSelfLocked.get(callerPid); - if (pr == null) { - Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid " - + callerPid); - return; - } - if (!pr.whitelistManager) { - if (DEBUG_WHITELISTS) { - Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid - + ": pid " + callerPid + " is not allowed"); - } - return; - } - } - } - - final long token = Binder.clearCallingIdentity(); - try { - mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(targetUid, duration, - true, "pe from uid:" + callerUid); - } finally { - Binder.restoreCallingIdentity(token); - } - } - @Override public void cancelIntentSender(IIntentSender sender) { if (!(sender instanceof PendingIntentRecord)) { @@ -7863,7 +7849,14 @@ public class ActivityManagerService extends IActivityManager.Stub r.pictureInPictureArgs.copyOnlySet(args); final float aspectRatio = r.pictureInPictureArgs.getAspectRatio(); final List<RemoteAction> actions = r.pictureInPictureArgs.getActions(); - final Rect sourceBounds = r.pictureInPictureArgs.getSourceRectHint(); + // Adjust the source bounds by the insets for the transition down + final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint()); + final Rect insets = r.pictureInPictureArgs.getSourceRectHintInsets(); + if (insets != null) { + sourceBounds.offsetTo(Math.max(0, sourceBounds.left - insets.left), + Math.max(0, sourceBounds.top - insets.top)); + } + mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio, true /* moveHomeStackToFront */, "enterPictureInPictureMode"); final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID); @@ -8412,7 +8405,8 @@ public class ActivityManagerService extends IActivityManager.Stub boolean isOnDeviceIdleWhitelistLocked(int uid) { final int appId = UserHandle.getAppId(uid); return Arrays.binarySearch(mDeviceIdleWhitelist, appId) >= 0 - || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0; + || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0 + || mPendingTempWhitelist.indexOfKey(uid) >= 0; } private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) { @@ -10535,8 +10529,9 @@ public class ActivityManagerService extends IActivityManager.Stub final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID); if (pinnedStack != null) { - pinnedStack.animateResizePinnedStack(null /* sourceBounds */, - destBounds, animationDuration); + pinnedStack.animateResizePinnedStack(null /* sourceHintBounds */, + destBounds, animationDuration, + false /* schedulePipModeChangedOnAnimationEnd */); } } else { throw new IllegalArgumentException("Stack: " + stackId @@ -10694,6 +10689,13 @@ public class ActivityManagerService extends IActivityManager.Stub return; } + // When a task is locked, dismiss the pinned stack if it exists + final PinnedActivityStack pinnedStack = mStackSupervisor.getStack( + PINNED_STACK_ID); + if (pinnedStack != null) { + mStackSupervisor.removeStackLocked(PINNED_STACK_ID); + } + // isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode // is initiated by system after the pinning request was shown and locked mode is initiated // by an authorized app directly @@ -11254,6 +11256,10 @@ public class ActivityManagerService extends IActivityManager.Stub holder.provider = null; return holder; } + // Don't expose instant app providers + if (cpr.appInfo.isInstantApp()) { + return null; + } final long origId = Binder.clearCallingIdentity(); @@ -15587,6 +15593,18 @@ public class ActivityManagerService extends IActivityManager.Stub } pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist)); + if (mPendingTempWhitelist.size() > 0) { + pw.println(" mPendingTempWhitelist:"); + for (int i = 0; i < mPendingTempWhitelist.size(); i++) { + PendingTempWhitelist ptw = mPendingTempWhitelist.valueAt(i); + pw.print(" "); + UserHandle.formatUid(pw, ptw.targetUid); + pw.print(": "); + TimeUtils.formatDuration(ptw.duration, pw); + pw.print(" "); + pw.println(ptw.tag); + } + } } if (dumpPackage == null) { pw.println(" mWakefulness=" @@ -17928,8 +17946,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ComponentName startService(IApplicationThread caller, Intent service, - String resolvedType, int id, Notification notification, boolean requireForeground, - String callingPackage, int userId) + String resolvedType, boolean requireForeground, String callingPackage, int userId) throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); // Refuse possible leaked file descriptors @@ -17950,7 +17967,7 @@ public class ActivityManagerService extends IActivityManager.Stub ComponentName res; try { res = mServices.startServiceLocked(caller, service, - resolvedType, id, notification, callingPid, callingUid, + resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId); } finally { Binder.restoreCallingIdentity(origId); @@ -17969,7 +17986,7 @@ public class ActivityManagerService extends IActivityManager.Stub ComponentName res; try { res = mServices.startServiceLocked(null, service, - resolvedType, 0, null, -1, uid, fgRequired, callingPackage, userId); + resolvedType, -1, uid, fgRequired, callingPackage, userId); } finally { Binder.restoreCallingIdentity(origId); } @@ -22699,6 +22716,80 @@ public class ActivityManagerService extends IActivityManager.Stub enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE); } + /** + * Whitelists {@code targetUid} to temporarily bypass Power Save mode. + */ + void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid, + long duration, String tag) { + if (DEBUG_WHITELISTS) { + Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", " + + targetUid + ", " + duration + ")"); + } + + synchronized (mPidsSelfLocked) { + final ProcessRecord pr = mPidsSelfLocked.get(callerPid); + if (pr == null) { + Slog.w(TAG, "tempWhitelistForPendingIntentLocked() no ProcessRecord for pid " + + callerPid); + return; + } + if (!pr.whitelistManager) { + if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid) + != PackageManager.PERMISSION_GRANTED) { + if (DEBUG_WHITELISTS) { + Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid + + ": pid " + callerPid + " is not allowed"); + } + return; + } + } + } + + tempWhitelistUidLocked(targetUid, duration, tag); + } + + /** + * Whitelists {@code targetUid} to temporarily bypass Power Save mode. + */ + void tempWhitelistUidLocked(int targetUid, long duration, String tag) { + mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag)); + setUidTempWhitelistStateLocked(targetUid, true); + mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget(); + } + + void pushTempWhitelist() { + final int N; + final PendingTempWhitelist[] list; + + // First copy out the pending changes... we need to leave them in the map for now, + // in case someone needs to check what is coming up while we don't have the lock held. + synchronized(this) { + N = mPendingTempWhitelist.size(); + list = new PendingTempWhitelist[N]; + for (int i = 0; i < N; i++) { + list[i] = mPendingTempWhitelist.valueAt(i); + } + } + + // Now safely dispatch changes to device idle controller. + for (int i = 0; i < N; i++) { + PendingTempWhitelist ptw = list[i]; + mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, + ptw.duration, true, ptw.tag); + } + + // And now we can safely remove them from the map. + synchronized(this) { + for (int i = 0; i < N; i++) { + PendingTempWhitelist ptw = list[i]; + int index = mPendingTempWhitelist.indexOfKey(ptw.targetUid); + if (index >= 0 && mPendingTempWhitelist.valueAt(index) == ptw) { + mPendingTempWhitelist.removeAt(index); + } + } + } + } + final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) { boolean changed = false; for (int i=mActiveUids.size()-1; i>=0; i--) { @@ -22713,6 +22804,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } + final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) { + boolean changed = false; + final UidRecord uidRec = mActiveUids.get(uid); + if (uidRec != null && uidRec.curWhitelist != onWhitelist) { + uidRec.curWhitelist = onWhitelist; + updateOomAdjLocked(); + } + } + final void trimApplications() { synchronized (this) { int i; diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 0fcf3e66debe..b6bfb00d3815 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -524,7 +524,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println("Starting service: " + intent); pw.flush(); ComponentName cn = mInterface.startService(null, intent, intent.getType(), - -1, null, asForeground, SHELL_PACKAGE_NAME, mUserId); + asForeground, SHELL_PACKAGE_NAME, mUserId); if (cn == null) { err.println("Error: Not found; no service started."); return -1; diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 28817878373b..494aaa766b94 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -314,7 +314,8 @@ class ActivityMetricsLogger { builder.setPackageName(info.launchedActivity.packageName); builder.setType(type); builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name); - if (info.launchedActivity.launchedFromPackage != null) { + final boolean isInstantApp = info.launchedActivity.info.applicationInfo.isInstantApp(); + if (isInstantApp && info.launchedActivity.launchedFromPackage != null) { builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME, info.launchedActivity.launchedFromPackage); } @@ -323,8 +324,7 @@ class ActivityMetricsLogger { info.launchedActivity.info.launchToken); info.launchedActivity.info.launchToken = null; } - builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, - info.launchedActivity.info.applicationInfo.isInstantApp() ? 1 : 0); + builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0); builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS, mCurrentTransitionDeviceUptime); builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 17c7dde11726..fe03c367814e 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1178,6 +1178,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * the activity is not currently visible and {@param noThrow} is not set. */ boolean checkEnterPictureInPictureState(String caller, boolean noThrow, boolean beforeStopping) { + if (!supportsPictureInPicture()) { + return false; + } + // Check app-ops and see if PiP is supported for this package if (!checkEnterPictureInPictureAppOpsState()) { return false; @@ -2126,6 +2130,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (mWindowContainerController == null) { return; } + if (mTaskOverlay) { + // We don't show starting window for overlay activities. + return; + } + final CompatibilityInfo compatInfo = service.compatibilityInfoForPackageLocked(info.applicationInfo); final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme, diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 4c84d98d468a..85c5c647ceed 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -65,6 +65,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; +import static com.android.server.am.ActivityStack.ActivityState.STOPPED; +import static com.android.server.am.ActivityStack.ActivityState.STOPPING; import static com.android.server.am.ActivityStackSupervisor.FindTaskResult; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY; @@ -589,6 +591,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } /** + * Returns whether to defer the scheduling of the multi-window mode. + */ + boolean deferScheduleMultiWindowModeChanged() { + return false; + } + + /** * Defers updating the bounds of the stack. If the stack was resized/repositioned while * deferring, the bounds will update in {@link #continueUpdateBounds()}. */ @@ -1172,7 +1181,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); - if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED + if (r.state == STOPPING || r.state == STOPPED || r.state == ActivityState.PAUSED || r.state == ActivityState.PAUSING) { r.setSleeping(true); } @@ -1355,7 +1364,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev); if (prev != null) { - final boolean wasStopping = prev.state == ActivityState.STOPPING; + final boolean wasStopping = prev.state == STOPPING; prev.state = ActivityState.PAUSED; if (prev.finishing) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev); @@ -1376,7 +1385,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. - prev.state = ActivityState.STOPPING; + prev.state = STOPPING; } else if ((!prev.visible && !hasVisibleBehindActivity()) || mService.isSleepingOrShuttingDownLocked()) { // If we were visible then resumeTopActivities will release resources before @@ -1995,10 +2004,17 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // keeping the screen frozen. if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state); try { + final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( + "makeInvisible", true /* noThrow */, true /* beforeStopping */); + // We don't want to call setVisible(false) to avoid notifying the client of this + // intermittent invisible state if it can enter Pip and isn't stopped or stopping. + if (!canEnterPictureInPicture || r.state == STOPPING || r.state == STOPPED) { + r.setVisible(false); + } + switch (r.state) { case STOPPING: case STOPPED: - r.setVisible(false); if (r.app != null && r.app.thread != null) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + r); @@ -2017,25 +2033,16 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // This case created for transitioning activities from // translucent to opaque {@link Activity#convertToOpaque}. if (visibleBehind == r) { - r.setVisible(false); releaseBackgroundResources(r); } else { // If this activity is in a state where it can currently enter // picture-in-picture, then don't immediately schedule the idle now in case // the activity tries to enterPictureInPictureMode() later. Otherwise, // we will try and stop the activity next time idle is processed. - final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( - "makeInvisible", true /* noThrow */, true /* beforeStopping */); if (canEnterPictureInPicture) { - // We set r.visible=false so that Stop will later - // call setVisible for us. In this case - // we don't want to call setVisible(false) to avoid - // notifying the client of this intermittent invisible - // state. + // We set r.visible=false so that Stop will later call setVisible for us r.visible = false; - } else { - r.setVisible(false); } addToStopping(r, true /* scheduleIdle */, canEnterPictureInPicture /* idleDelayed */); @@ -2311,9 +2318,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid); + final boolean prevCanPip = prev != null && prev.checkEnterPictureInPictureState( + "resumeTopActivity", true /* noThrow */, userLeaving /* beforeStopping */); // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity - // to be paused, while at the same time resuming the new resume activity + // to be paused, while at the same time resuming the new resume activity only if the + // previous activity can't go into Pip since we want to give Pip activities a chance to + // enter Pip before resuming the next activity. final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0; + // TODO: This would be go to have however, the various call points that pass in + // prev need to be corrected first. In some cases the prev is equal to the next e.g. launch + // an app from home. And, is come other cases it is null e.g. press home button after + // launching an app. The doc on the method says prev. is null expect for the case we are + // coming from pause. We need to see if that is a valid thing and also if all the code in + // this method using prev. are setup to function like that. + //&& !prevCanPip; boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false); if (mResumedActivity != null) { if (DEBUG_STATES) Slog.d(TAG_STATES, @@ -3353,11 +3371,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.stopped = false; if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: " + r + " (stop requested)"); - r.state = ActivityState.STOPPING; + r.state = STOPPING; if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Stopping visible=" + r.visible + " for " + r); if (!r.visible) { - r.setVisibility(false); + r.setVisible(false); } EventLogTags.writeAmStopActivity( r.userId, System.identityHashCode(r), r.shortComponentName); @@ -3375,7 +3393,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Just in case, assume it to be stopped. r.stopped = true; if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r); - r.state = ActivityState.STOPPED; + r.state = STOPPED; if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-except"); } @@ -3680,7 +3698,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: "+ r + " (finish requested)"); - r.state = ActivityState.STOPPING; + r.state = STOPPING; if (oomAdj) { mService.updateOomAdjLocked(); } @@ -3705,8 +3723,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || (prevState == ActivityState.PAUSED && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID)) || finishingActivityInNonFocusedStack - || prevState == ActivityState.STOPPING - || prevState == ActivityState.STOPPED + || prevState == STOPPING + || prevState == STOPPED || prevState == ActivityState.INITIALIZING) { r.makeFinishingLocked(); boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm"); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 4d16e33ad81c..ab703409a393 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2397,10 +2397,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } else { for (int i = 0; i < size; i++) { final TaskRecord task = tasks.get(i); - final int position = fullscreenStack != null - ? Math.max(fullscreenStack.getAllTasks().size() - 1, 0) : 0; - // Defer resume until all the tasks have been moved to the fullscreen stack - task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, position, + // Position the tasks in the fullscreen stack in order at the bottom of the + // stack. Also defer resume until all the tasks have been moved to the + // fullscreen stack. + task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, i /* position */, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, schedulePictureInPictureModeChange, "moveTasksToFullscreenStack - NOT_onTop"); @@ -2504,7 +2504,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting // for the AMS lock to be freed. So check and make sure these bounds are still good. final PinnedStackWindowController stackController = stack.getWindowContainerController(); - if (stackController.pinnedStackResizeAllowed()) { + if (stackController.pinnedStackResizeDisallowed()) { return; } @@ -2873,11 +2873,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, float aspectRatio, + void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio, boolean moveHomeStackToFront, String reason) { mWindowManager.deferSurfaceLayout(); + // This will clear the pinned stack by moving an existing task to the full screen stack, + // ensuring only one task is present. + moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP); + // Need to make sure the pinned stack exist so we can resize it below... final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP); @@ -2948,11 +2952,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY, aspectRatio, false /* useExistingStackBounds */); - // TODO(b/36099777): Schedule the PiP mode change here immediately until we can defer all - // callbacks until after the bounds animation - scheduleUpdatePictureInPictureModeIfNeeded(r.getTask(), destBounds, true /* immediate */); - - stack.animateResizePinnedStack(sourceBounds, destBounds, -1 /* animationDuration */); + stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, + true /* schedulePipModeChangedOnAnimationEnd */); mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName); } @@ -4179,6 +4180,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } void scheduleUpdateMultiWindowMode(TaskRecord task) { + // If the stack is animating in a way where we will be forcing a multi-mode change at the + // end, then ensure that we defer all in between multi-window mode changes + if (task.getStack().deferScheduleMultiWindowModeChanged()) { + return; + } + for (int i = task.mActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = task.mActivities.get(i); if (r.app != null && r.app.thread != null) { diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index 02ec07561f4f..c9c1d005a85a 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -148,18 +148,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { - final int result = msg.what; - - synchronized (mService) { - if (mProc != null && mProc.crashDialog == AppErrorDialog.this) { - mProc.crashDialog = null; - } - } - mResult.set(result); - - // Make sure we don't have time timeout still hanging around. - removeMessages(TIMEOUT); - + setResult(msg.what); dismiss(); } }; @@ -168,11 +157,23 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen public void dismiss() { if (!mResult.mHasResult) { // We are dismissing and the result has not been set...go ahead and set. - mResult.set(FORCE_QUIT); + setResult(FORCE_QUIT); } super.dismiss(); } + private void setResult(int result) { + synchronized (mService) { + if (mProc != null && mProc.crashDialog == AppErrorDialog.this) { + mProc.crashDialog = null; + } + } + mResult.set(result); + + // Make sure we don't have time timeout still hanging around. + mHandler.removeMessages(TIMEOUT); + } + @Override public void onClick(View v) { switch (v.getId()) { diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index baa71d708caa..349180fd28a9 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -155,8 +155,6 @@ public final class BroadcastQueue { static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG; static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1; - static final int SCHEDULE_TEMP_WHITELIST_MSG - = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 2; final BroadcastHandler mHandler; @@ -178,13 +176,6 @@ public final class BroadcastQueue { broadcastTimeoutLocked(true); } } break; - case SCHEDULE_TEMP_WHITELIST_MSG: { - DeviceIdleController.LocalService dic = mService.mLocalDeviceIdleController; - if (dic != null) { - dic.addPowerSaveTempWhitelistAppDirect(UserHandle.getAppId(msg.arg1), - msg.arg2, true, (String)msg.obj); - } - } break; } } } @@ -789,12 +780,11 @@ public final class BroadcastQueue { if (r.intent.getAction() != null) { b.append(r.intent.getAction()); } else if (r.intent.getComponent() != null) { - b.append(r.intent.getComponent().flattenToShortString()); + r.intent.getComponent().appendShortString(b); } else if (r.intent.getData() != null) { b.append(r.intent.getData()); } - mHandler.obtainMessage(SCHEDULE_TEMP_WHITELIST_MSG, uid, (int)duration, b.toString()) - .sendToTarget(); + mService.tempWhitelistUidLocked(uid, duration, b.toString()); } /** diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index c697f2876c65..a580d4bdde5e 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -237,14 +237,6 @@ final class PendingIntentRecord extends IIntentSender.Stub { if (intent != null) intent.setDefusable(true); if (options != null) options.setDefusable(true); - if (whitelistDuration > 0 && !canceled) { - // Must call before acquiring the lock. It's possible the method return before sending - // the intent due to some validations inside the lock, in which case the UID shouldn't - // be whitelisted, but since the whitelist is temporary, that would be ok. - owner.tempWhitelistAppForPowerSave(Binder.getCallingPid(), Binder.getCallingUid(), uid, - whitelistDuration); - } - synchronized (owner) { final ActivityContainer activityContainer = (ActivityContainer)container; if (activityContainer != null && activityContainer.mParentActivity != null && @@ -279,6 +271,22 @@ final class PendingIntentRecord extends IIntentSender.Stub { resolvedType = key.requestResolvedType; } + if (whitelistDuration > 0) { + StringBuilder tag = new StringBuilder(64); + tag.append("pendingintent:"); + UserHandle.formatUid(tag, Binder.getCallingUid()); + tag.append(":"); + if (finalIntent.getAction() != null) { + tag.append(finalIntent.getAction()); + } else if (finalIntent.getComponent() != null) { + finalIntent.getComponent().appendShortString(tag); + } else if (finalIntent.getData() != null) { + tag.append(finalIntent.getData()); + } + owner.tempWhitelistForPendingIntentLocked(Binder.getCallingPid(), + Binder.getCallingUid(), uid, whitelistDuration, tag.toString()); + } + final long origId = Binder.clearCallingIdentity(); boolean sendFinish = finishedReceiver != null; diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java index cd9c42c7c643..a4932bbdd732 100644 --- a/services/core/java/com/android/server/am/PinnedActivityStack.java +++ b/services/core/java/com/android/server/am/PinnedActivityStack.java @@ -43,9 +43,10 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds); } - void animateResizePinnedStack(Rect sourceBounds, Rect destBounds, int animationDuration) { - getWindowContainerController().animateResizePinnedStack(sourceBounds, destBounds, - animationDuration); + void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration, + boolean schedulePipModeChangedOnAnimationEnd) { + getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds, + animationDuration, schedulePipModeChangedOnAnimationEnd); } void setPictureInPictureAspectRatio(float aspectRatio) { @@ -60,7 +61,18 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> return getWindowContainerController().isAnimatingBoundsToFullscreen(); } - @Override + /** + * Returns whether to defer the scheduling of the multi-window mode. + */ + boolean deferScheduleMultiWindowModeChanged() { + // For the pinned stack, the deferring of the multi-window mode changed is tied to the + // transition animation into picture-in-picture, and is called once the animation completes, + // or is interrupted in a way that would leave the stack in a non-fullscreen state. + // @see BoundsAnimationController + // @see BoundsAnimationControllerTests + return mWindowContainerController.deferScheduleMultiWindowModeChanged(); + } + public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) { // It is guaranteed that the activities requiring the update will be in the pinned stack at // this point (either reparented before the animation into PiP, or before reparenting after diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index f8a4d4b21414..d42b6a7647ce 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -48,6 +48,7 @@ import android.service.voice.IVoiceInteractionSession; import android.util.DisplayMetrics; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.util.XmlUtils; @@ -445,10 +446,23 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta final Rect bounds = updateOverrideConfigurationFromLaunchBounds(); final Configuration overrideConfig = getOverrideConfiguration(); - mWindowContainerController = new TaskWindowContainerController(taskId, this, + setWindowContainerController(new TaskWindowContainerController(taskId, this, getStack().getWindowContainerController(), userId, bounds, overrideConfig, mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers, - lastTaskDescription); + lastTaskDescription)); + } + + /** + * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}. + */ + @VisibleForTesting + protected void setWindowContainerController(TaskWindowContainerController controller) { + if (mWindowContainerController != null) { + throw new IllegalArgumentException("Window container=" + mWindowContainerController + + " already created for task=" + this); + } + + mWindowContainerController = controller; } void removeWindowContainer() { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 268724283351..aa1b74ce4577 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -520,7 +520,11 @@ public class AudioService extends IAudioService.Stub private int mPrevVolDirection = AudioManager.ADJUST_SAME; // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume // is controlled by Vol keys. - private int mVolumeControlStream = -1; + private int mVolumeControlStream = -1; + // interpretation of whether the volume stream has been selected by the user by clicking on a + // volume slider to change which volume is controlled by the volume keys. Is false + // when mVolumeControlStream is -1. + private boolean mUserSelectedVolumeControlStream = false; private final Object mForceControlStreamLock = new Object(); // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system // server process so in theory it is not necessary to monitor the client death. @@ -929,11 +933,8 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = 0; streamType < numStreamTypes; streamType++) { - if (streamType != mStreamVolumeAlias[streamType]) { - mStreamStates[streamType]. - setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], - TAG); - } + mStreamStates[streamType] + .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG); // apply stream volume if (!mStreamStates[streamType].mIsMuted) { mStreamStates[streamType].applyAllVolumes(); @@ -1022,20 +1023,21 @@ public class AudioService extends IAudioService.Stub } mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias; - final int oldStreamA11yAlias = mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY]; - if (oldStreamA11yAlias != a11yStreamAlias) { - mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias; - mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName = - System.VOLUME_SETTINGS_INT[a11yStreamAlias]; - // restore the value from the settings when the alias changes - mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings(); - } + mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias; if (updateVolumes) { mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias], caller); + + mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName = + System.VOLUME_SETTINGS_INT[a11yStreamAlias]; mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes( mStreamStates[a11yStreamAlias], caller); + if (sIndependentA11yVolume) { + // restore the a11y values from the settings + mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings(); + } + // apply stream mute states according to new value of mRingerModeAffectedStreams setRingerModeInt(getRingerModeInternal(), false); sendMsg(mAudioHandler, @@ -1226,14 +1228,29 @@ public class AudioService extends IAudioService.Stub private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller, int uid) { if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType - + ", flags=" + flags + ", caller=" + caller); - int streamType; - boolean isMute = isMuteAdjust(direction); - if (mVolumeControlStream != -1) { + + ", flags=" + flags + ", caller=" + caller + + ", volControlStream=" + mVolumeControlStream + + ", userSelect=" + mUserSelectedVolumeControlStream); + final int streamType; + if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1 streamType = mVolumeControlStream; } else { - streamType = getActiveStreamType(suggestedStreamType); + final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType); + final boolean activeForReal; + if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) { + activeForReal = isAfMusicActiveRecently(0); + } else { + activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0); + } + if (activeForReal || mVolumeControlStream == -1) { + streamType = maybeActiveStreamType; + } else { + streamType = mVolumeControlStream; + } } + + final boolean isMute = isMuteAdjust(direction); + ensureValidStreamType(streamType); final int resolvedStream = mStreamVolumeAlias[streamType]; @@ -1709,13 +1726,18 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#forceVolumeControlStream(int) */ public void forceVolumeControlStream(int streamType, IBinder cb) { + if (DEBUG_VOL) { Log.d(TAG, String.format("forceVolumeControlStream(%d)", streamType)); } synchronized(mForceControlStreamLock) { + if (mVolumeControlStream != -1 && streamType != -1) { + mUserSelectedVolumeControlStream = true; + } mVolumeControlStream = streamType; if (mVolumeControlStream == -1) { if (mForceControlStreamClient != null) { mForceControlStreamClient.release(); mForceControlStreamClient = null; } + mUserSelectedVolumeControlStream = false; } else { mForceControlStreamClient = new ForceControlStreamClient(cb); } @@ -1746,6 +1768,7 @@ public class AudioService extends IAudioService.Stub } else { mForceControlStreamClient = null; mVolumeControlStream = -1; + mUserSelectedVolumeControlStream = false; } } } @@ -4228,7 +4251,17 @@ public class AudioService extends IAudioService.Stub return mIndexMin; } + /** + * Copies all device/index pairs from the given VolumeStreamState after initializing + * them with the volume for DEVICE_OUT_DEFAULT. No-op if the source VolumeStreamState + * has the same stream type as this instance. + * @param srcStream + * @param caller + */ public void setAllIndexes(VolumeStreamState srcStream, String caller) { + if (mStreamType == srcStream.mStreamType) { + return; + } synchronized (VolumeStreamState.class) { int srcStreamType = srcStream.getStreamType(); // apply default device volume from source stream to all devices first in case diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 1ffa86440ae2..601ed010a696 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -78,7 +78,6 @@ public class TetherInterfaceStateMachine extends StateMachine { public static final int CMD_IPV6_TETHER_UPDATE = BASE_IFACE + 13; private final State mInitialState; - private final State mServingState; private final State mLocalHotspotState; private final State mTetheredState; private final State mUnavailableState; @@ -107,14 +106,12 @@ public class TetherInterfaceStateMachine extends StateMachine { mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; mInitialState = new InitialState(); - mServingState = new ServingState(); mLocalHotspotState = new LocalHotspotState(); mTetheredState = new TetheredState(); mUnavailableState = new UnavailableState(); addState(mInitialState); - addState(mServingState); - addState(mLocalHotspotState, mServingState); - addState(mTetheredState, mServingState); + addState(mLocalHotspotState); + addState(mTetheredState); addState(mUnavailableState); setInitialState(mInitialState); @@ -222,12 +219,11 @@ public class TetherInterfaceStateMachine extends StateMachine { } } - class ServingState extends State { + class BaseServingState extends State { @Override public void enter() { if (!configureIfaceIp(true)) { mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR; - transitionTo(mInitialState); return; } @@ -236,12 +232,13 @@ public class TetherInterfaceStateMachine extends StateMachine { } catch (Exception e) { Log.e(TAG, "Error Tethering: " + e.toString()); mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; - transitionTo(mInitialState); return; } if (!mIPv6TetherSvc.start()) { Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices"); + // TODO: Make this a fatal error once Bluetooth IPv6 is sorted. + return; } } @@ -254,9 +251,9 @@ public class TetherInterfaceStateMachine extends StateMachine { try { mNMService.untetherInterface(mIfaceName); - } catch (Exception ee) { + } catch (Exception e) { mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; - Log.e(TAG, "Failed to untether interface: " + ee.toString()); + Log.e(TAG, "Failed to untether interface: " + e.toString()); } configureIfaceIp(false); @@ -293,15 +290,27 @@ public class TetherInterfaceStateMachine extends StateMachine { } } - class LocalHotspotState extends State { + // Handling errors in BaseServingState.enter() by transitioning is + // problematic because transitioning during a multi-state jump yields + // a Log.wtf(). Ultimately, there should be only one ServingState, + // and forwarding and NAT rules should be handled by a coordinating + // functional element outside of TetherInterfaceStateMachine. + class LocalHotspotState extends BaseServingState { @Override public void enter() { + super.enter(); + if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + transitionTo(mInitialState); + } + if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName); sendInterfaceState(IControlsTethering.STATE_LOCAL_HOTSPOT); } @Override public boolean processMessage(Message message) { + if (super.processMessage(message)) return true; + maybeLogMessage(this, message.what); switch (message.what) { case CMD_TETHER_REQUESTED: @@ -317,9 +326,19 @@ public class TetherInterfaceStateMachine extends StateMachine { } } - class TetheredState extends State { + // Handling errors in BaseServingState.enter() by transitioning is + // problematic because transitioning during a multi-state jump yields + // a Log.wtf(). Ultimately, there should be only one ServingState, + // and forwarding and NAT rules should be handled by a coordinating + // functional element outside of TetherInterfaceStateMachine. + class TetheredState extends BaseServingState { @Override public void enter() { + super.enter(); + if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + transitionTo(mInitialState); + } + if (DBG) Log.d(TAG, "Tethered " + mIfaceName); sendInterfaceState(IControlsTethering.STATE_TETHERED); } @@ -327,6 +346,7 @@ public class TetherInterfaceStateMachine extends StateMachine { @Override public void exit() { cleanupUpstream(); + super.exit(); } private void cleanupUpstream() { @@ -361,6 +381,8 @@ public class TetherInterfaceStateMachine extends StateMachine { @Override public boolean processMessage(Message message) { + if (super.processMessage(message)) return true; + maybeLogMessage(this, message.what); boolean retValue = true; switch (message.what) { diff --git a/services/core/java/com/android/server/job/GrantedUriPermissions.java b/services/core/java/com/android/server/job/GrantedUriPermissions.java new file mode 100644 index 000000000000..e413d8d1d8c0 --- /dev/null +++ b/services/core/java/com/android/server/job/GrantedUriPermissions.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.job; + +import android.app.IActivityManager; +import android.content.ClipData; +import android.content.ContentProvider; +import android.content.Intent; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Slog; + +import java.io.PrintWriter; +import java.util.ArrayList; + +public class GrantedUriPermissions { + private final int mGrantFlags; + private final int mSourceUserId; + private final String mTag; + private final IBinder mPermissionOwner; + private final ArrayList<Uri> mUris = new ArrayList<>(); + + private GrantedUriPermissions(IActivityManager am, int grantFlags, int uid, String tag) + throws RemoteException { + mGrantFlags = grantFlags; + mSourceUserId = UserHandle.getUserId(uid); + mTag = tag; + mPermissionOwner = am.newUriPermissionOwner("job: " + tag); + } + + public void revoke(IActivityManager am) { + for (int i = mUris.size()-1; i >= 0; i--) { + try { + am.revokeUriPermissionFromOwner(mPermissionOwner, mUris.get(i), + mGrantFlags, mSourceUserId); + } catch (RemoteException e) { + } + } + mUris.clear(); + } + + public static boolean checkGrantFlags(int grantFlags) { + return (grantFlags & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION + |Intent.FLAG_GRANT_READ_URI_PERMISSION)) != 0; + } + + public static GrantedUriPermissions createFromIntent(IActivityManager am, Intent intent, + int sourceUid, String targetPackage, int targetUserId, String tag) { + int grantFlags = intent.getFlags(); + if (!checkGrantFlags(grantFlags)) { + return null; + } + + GrantedUriPermissions perms = null; + + Uri data = intent.getData(); + if (data != null) { + perms = grantUri(am, data, sourceUid, targetPackage, targetUserId, grantFlags, tag, + perms); + } + + ClipData clip = intent.getClipData(); + if (clip != null) { + perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags, tag, + perms); + } + + return perms; + } + + public static GrantedUriPermissions createFromClip(IActivityManager am, ClipData clip, + int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag) { + if (!checkGrantFlags(grantFlags)) { + return null; + } + GrantedUriPermissions perms = null; + if (clip != null) { + perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags, + tag, perms); + } + return perms; + } + + private static GrantedUriPermissions grantClip(IActivityManager am, ClipData clip, + int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag, + GrantedUriPermissions curPerms) { + final int N = clip.getItemCount(); + for (int i = 0; i < N; i++) { + curPerms = grantItem(am, clip.getItemAt(i), sourceUid, targetPackage, targetUserId, + grantFlags, tag, curPerms); + } + return curPerms; + } + + private static GrantedUriPermissions grantUri(IActivityManager am, Uri uri, + int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag, + GrantedUriPermissions curPerms) { + try { + int sourceUserId = ContentProvider.getUserIdFromUri(uri, + UserHandle.getUserId(sourceUid)); + uri = ContentProvider.getUriWithoutUserId(uri); + if (curPerms == null) { + curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag); + } + am.grantUriPermissionFromOwner(curPerms.mPermissionOwner, sourceUid, targetPackage, + uri, grantFlags, sourceUserId, targetUserId); + curPerms.mUris.add(uri); + } catch (RemoteException e) { + Slog.e("JobScheduler", "AM dead"); + } + return curPerms; + } + + private static GrantedUriPermissions grantItem(IActivityManager am, ClipData.Item item, + int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag, + GrantedUriPermissions curPerms) { + if (item.getUri() != null) { + curPerms = grantUri(am, item.getUri(), sourceUid, targetPackage, targetUserId, + grantFlags, tag, curPerms); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + curPerms = grantUri(am, intent.getData(), sourceUid, targetPackage, targetUserId, + grantFlags, tag, curPerms); + } + return curPerms; + } + + // Dumpsys infrastructure + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags)); + pw.print(" mSourceUserId="); pw.println(mSourceUserId); + pw.print(prefix); pw.print("mTag="); pw.println(mTag); + pw.print(prefix); pw.print("mPermissionOwner="); pw.println(mPermissionOwner); + for (int i = 0; i < mUris.size(); i++) { + pw.print(prefix); pw.print("#"); pw.print(i); pw.print(": "); + pw.println(mUris.get(i)); + } + } +} diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index d01de3c9157d..c8bfa345af10 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -601,7 +601,7 @@ public final class JobSchedulerService extends com.android.server.SystemService // Fast path: we are adding work to an existing job, and the JobInfo is not // changing. We can just directly enqueue this work in to the job. if (toCancel.getJob().equals(job)) { - toCancel.enqueueWorkLocked(work); + toCancel.enqueueWorkLocked(ActivityManager.getService(), work); return JobScheduler.RESULT_SUCCESS; } } @@ -625,7 +625,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } if (work != null) { // If work has been supplied, enqueue it into the new job. - jobStatus.enqueueWorkLocked(work); + jobStatus.enqueueWorkLocked(ActivityManager.getService(), work); } startTrackingJobLocked(jobStatus, toCancel); mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); @@ -758,7 +758,7 @@ public final class JobSchedulerService extends com.android.server.SystemService final JobStatus executing = jsc.getRunningJob(); if (executing != null && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) { - jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE); + jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE); } } } else { @@ -921,7 +921,7 @@ public final class JobSchedulerService extends com.android.server.SystemService private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean writeBack) { // Deal with any remaining work items in the old job. - jobStatus.stopTrackingJobLocked(incomingJob); + jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob); // Remove from store as well as controllers. final boolean removed = mJobs.remove(jobStatus, writeBack); @@ -939,7 +939,7 @@ public final class JobSchedulerService extends com.android.server.SystemService JobServiceContext jsc = mActiveServices.get(i); final JobStatus executing = jsc.getRunningJob(); if (executing != null && executing.matches(job.getUid(), job.getJobId())) { - jsc.cancelExecutingJob(reason); + jsc.cancelExecutingJobLocked(reason); return true; } } @@ -1071,9 +1071,16 @@ public final class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); } + + // If the job wants to be rescheduled, we first need to make the next upcoming + // job so we can transfer any appropriate state over from the previous job when + // we stop it. + final JobStatus rescheduledJob = needsReschedule + ? getRescheduleJobForFailureLocked(jobStatus) : null; + // Do not write back immediately if this is a periodic job. The job may get lost if system // shuts down before it is added back. - if (!stopTrackingJobLocked(jobStatus, null, !jobStatus.getJob().isPeriodic())) { + if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) { if (DEBUG) { Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); } @@ -1082,18 +1089,14 @@ public final class JobSchedulerService extends com.android.server.SystemService mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); return; } - // Note: there is a small window of time in here where, when rescheduling a job, - // we will stop monitoring its content providers. This should be fixed by stopping - // the old job after scheduling the new one, but since we have no lock held here - // that may cause ordering problems if the app removes jobStatus while in here. - if (needsReschedule) { - JobStatus rescheduled = getRescheduleJobForFailureLocked(jobStatus); + + if (rescheduledJob != null) { try { - rescheduled.prepareLocked(ActivityManager.getService()); + rescheduledJob.prepareLocked(ActivityManager.getService()); } catch (SecurityException e) { - Slog.w(TAG, "Unable to regrant job permissions for " + rescheduled); + Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob); } - startTrackingJobLocked(rescheduled, jobStatus); + startTrackingJobLocked(rescheduledJob, jobStatus); } else if (jobStatus.getJob().isPeriodic()) { JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); try { @@ -1561,7 +1564,7 @@ public final class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob()); } // preferredUid will be set to uid of currently running job. - mActiveServices.get(i).preemptExecutingJob(); + mActiveServices.get(i).preemptExecutingJobLocked(); preservePreferredUid = true; } else { final JobStatus pendingJob = contextIdToJobMap[i]; diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index c7ef0e26971f..9144966da409 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -44,8 +44,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.server.job.controllers.JobStatus; -import java.util.concurrent.atomic.AtomicBoolean; - /** * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this * class. @@ -56,19 +54,15 @@ import java.util.concurrent.atomic.AtomicBoolean; * job lands, and again when it is complete. * - Cancelling is trickier, because there are also interactions from the client. It's possible * the {@link com.android.server.job.JobServiceContext.JobServiceHandler} tries to process a - * {@link #MSG_CANCEL} after the client has already finished. This is handled by having - * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelH} check whether + * {@link #doCancelLocked(int)} after the client has already finished. This is handled by having + * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelLocked} check whether * the context is still valid. - * To mitigate this, tearing down the context removes all messages from the handler, including any - * tardy {@link #MSG_CANCEL}s. Additionally, we avoid sending duplicate onStopJob() + * To mitigate this, we avoid sending duplicate onStopJob() * calls to the client after they've specified jobFinished(). */ public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection { private static final boolean DEBUG = JobSchedulerService.DEBUG; private static final String TAG = "JobServiceContext"; - /** Define the maximum # of jobs allowed to run on a service at once. */ - private static final int defaultMaxActiveJobsPerService = - ActivityManager.isLowRamDeviceStatic() ? 1 : 3; /** Amount of time a job is allowed to execute for before being considered timed-out. */ private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins. /** Amount of time the JobScheduler waits for the initial service launch+bind. */ @@ -90,14 +84,6 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne // Messages that result from interactions with the client service. /** System timed out waiting for a response. */ private static final int MSG_TIMEOUT = 0; - /** Received a callback from client. */ - private static final int MSG_CALLBACK = 1; - /** Run through list and start any ready jobs.*/ - private static final int MSG_SERVICE_BOUND = 2; - /** Cancel a job. */ - private static final int MSG_CANCEL = 3; - /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/ - private static final int MSG_SHUTDOWN_EXECUTION = 4; public static final int NO_PREFERRED_UID = -1; @@ -115,7 +101,7 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne private JobParameters mParams; @VisibleForTesting int mVerb; - private AtomicBoolean mCancelled = new AtomicBoolean(); + private boolean mCancelled; /** * All the information maintained about the job currently being executed. @@ -245,14 +231,12 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne } /** Called externally when a job that was scheduled for execution should be cancelled. */ - void cancelExecutingJob(int reason) { - mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget(); + void cancelExecutingJobLocked(int reason) { + doCancelLocked(reason); } - void preemptExecutingJob() { - Message m = mCallbackHandler.obtainMessage(MSG_CANCEL); - m.arg1 = JobParameters.REASON_PREEMPT; - m.sendToTarget(); + void preemptExecutingJobLocked() { + doCancelLocked(JobParameters.REASON_PREEMPT); } int getPreferredUid() { @@ -273,59 +257,54 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne @Override public void jobFinished(int jobId, boolean reschedule) { - if (!verifyCallingUid()) { - return; - } - mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0) - .sendToTarget(); + doCallback(reschedule); } @Override public void acknowledgeStopMessage(int jobId, boolean reschedule) { - if (!verifyCallingUid()) { - return; - } - mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0) - .sendToTarget(); + doCallback(reschedule); } @Override public void acknowledgeStartMessage(int jobId, boolean ongoing) { - if (!verifyCallingUid()) { - return; - } - mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget(); + doCallback(ongoing); } @Override public JobWorkItem dequeueWork(int jobId) { - if (!verifyCallingUid()) { - throw new SecurityException("Bad calling uid: " + Binder.getCallingUid()); - } - JobWorkItem work = null; - boolean stillWorking = false; - synchronized (mLock) { - if (mRunningJob != null) { - work = mRunningJob.dequeueWorkLocked(); - stillWorking = mRunningJob.hasExecutingWorkLocked(); + final int callingUid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (!verifyCallingUidLocked(callingUid)) { + throw new SecurityException("Bad calling uid: " + callingUid); + } + + final JobWorkItem work = mRunningJob.dequeueWorkLocked(); + if (work == null && !mRunningJob.hasExecutingWorkLocked()) { + // This will finish the job. + doCallbackLocked(false); + } + return work; } + } finally { + Binder.restoreCallingIdentity(ident); } - if (work == null && !stillWorking) { - jobFinished(jobId, false); - } - return work; } @Override public boolean completeWork(int jobId, int workId) { - if (!verifyCallingUid()) { - throw new SecurityException("Bad calling uid: " + Binder.getCallingUid()); - } - synchronized (mLock) { - if (mRunningJob != null) { - return mRunningJob.completeWorkLocked(workId); + final int callingUid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (!verifyCallingUidLocked(callingUid)) { + throw new SecurityException("Bad calling uid: " + callingUid); + } + return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId); } - return false; + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -344,20 +323,20 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne // looper and at this point we can't get any binder callbacks from the client. Better // safe than sorry. runningJob = mRunningJob; - } - if (runningJob == null || !name.equals(runningJob.getServiceComponent())) { - mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); - return; - } - this.service = IJobService.Stub.asInterface(service); - final PowerManager pm = - (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - runningJob.getTag()); - wl.setWorkSource(new WorkSource(runningJob.getSourceUid())); - wl.setReferenceCounted(false); - wl.acquire(); - synchronized (mLock) { + + if (runningJob == null || !name.equals(runningJob.getServiceComponent())) { + closeAndCleanupJobLocked(true /* needsReschedule */); + return; + } + this.service = IJobService.Stub.asInterface(service); + final PowerManager pm = + (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + runningJob.getTag()); + wl.setWorkSource(new WorkSource(runningJob.getSourceUid())); + wl.setReferenceCounted(false); + wl.acquire(); + // We use a new wakelock instance per job. In rare cases there is a race between // teardown following job completion/cancellation and new job service spin-up // such that if we simply assign mWakeLock to be the new instance, we orphan @@ -369,14 +348,16 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne mWakeLock.release(); } mWakeLock = wl; + doServiceBoundLocked(); } - mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); } /** If the client service crashes we reschedule this job and clean up. */ @Override public void onServiceDisconnected(ComponentName name) { - mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); + synchronized (mLock) { + closeAndCleanupJobLocked(true /* needsReschedule */); + } } /** @@ -384,22 +365,18 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne * whether the client exercising the callback is the client we expect. * @return True if the binder calling is coming from the client we expect. */ - private boolean verifyCallingUid() { - synchronized (mLock) { - if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { - if (DEBUG) { - Slog.d(TAG, "Stale callback received, ignoring."); - } - return false; + private boolean verifyCallingUidLocked(final int callingUid) { + if (mRunningJob == null || callingUid != mRunningJob.getUid()) { + if (DEBUG) { + Slog.d(TAG, "Stale callback received, ignoring."); } - return true; + return false; } + return true; } /** - * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this - * class is to append 'H' to each function name that can only be called on this handler. This - * isn't strictly necessary because all of these functions are private, but helps clarity. + * Scheduling of async messages (basically timeouts at this point). */ private class JobServiceHandler extends Handler { JobServiceHandler(Looper looper) { @@ -409,293 +386,277 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne @Override public void handleMessage(Message message) { switch (message.what) { - case MSG_SERVICE_BOUND: - doServiceBound(); - break; - case MSG_CALLBACK: - doCallback(message.arg2); - break; - case MSG_CANCEL: - doCancel(message.arg1); - break; case MSG_TIMEOUT: synchronized (mLock) { - handleOpTimeoutH(); + handleOpTimeoutLocked(); } break; - case MSG_SHUTDOWN_EXECUTION: - closeAndCleanupJobH(true /* needsReschedule */); - break; default: Slog.e(TAG, "Unrecognised message: " + message); } } + } - void doServiceBound() { + void doServiceBoundLocked() { + removeOpTimeOutLocked(); + handleServiceBoundLocked(); + } + + void doCallback(boolean reschedule) { + final int callingUid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { synchronized (mLock) { - removeOpTimeOutLocked(); - handleServiceBoundH(); + if (!verifyCallingUidLocked(callingUid)) { + return; + } + doCallbackLocked(reschedule); } + } finally { + Binder.restoreCallingIdentity(ident); } + } - void doCallback(int arg2) { - synchronized (mLock) { - if (DEBUG) { - Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob - + " v:" + VERB_STRINGS[mVerb]); - } - removeOpTimeOutLocked(); + void doCallbackLocked(boolean reschedule) { + if (DEBUG) { + Slog.d(TAG, "doCallback of : " + mRunningJob + + " v:" + VERB_STRINGS[mVerb]); + } + removeOpTimeOutLocked(); - if (mVerb == VERB_STARTING) { - final boolean workOngoing = arg2 == 1; - handleStartedH(workOngoing); - } else if (mVerb == VERB_EXECUTING || - mVerb == VERB_STOPPING) { - final boolean reschedule = arg2 == 1; - handleFinishedH(reschedule); - } else { - if (DEBUG) { - Slog.d(TAG, "Unrecognised callback: " + mRunningJob); - } - } + if (mVerb == VERB_STARTING) { + handleStartedLocked(reschedule); + } else if (mVerb == VERB_EXECUTING || + mVerb == VERB_STOPPING) { + handleFinishedLocked(reschedule); + } else { + if (DEBUG) { + Slog.d(TAG, "Unrecognised callback: " + mRunningJob); } } + } - void doCancel(int arg1) { - synchronized (mLock) { - if (mVerb == VERB_FINISHED) { - if (DEBUG) { - Slog.d(TAG, - "Trying to process cancel for torn-down context, ignoring."); - } - return; - } - mParams.setStopReason(arg1); - if (arg1 == JobParameters.REASON_PREEMPT) { - mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : - NO_PREFERRED_UID; - } - handleCancelH(); + void doCancelLocked(int arg1) { + if (mVerb == VERB_FINISHED) { + if (DEBUG) { + Slog.d(TAG, + "Trying to process cancel for torn-down context, ignoring."); } - + return; + } + mParams.setStopReason(arg1); + if (arg1 == JobParameters.REASON_PREEMPT) { + mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : + NO_PREFERRED_UID; } + handleCancelLocked(); + } - /** Start the job on the service. */ - private void handleServiceBoundH() { + /** Start the job on the service. */ + private void handleServiceBoundLocked() { + if (DEBUG) { + Slog.d(TAG, "handleServiceBound for " + mRunningJob.toShortString()); + } + if (mVerb != VERB_BINDING) { + Slog.e(TAG, "Sending onStartJob for a job that isn't pending. " + + VERB_STRINGS[mVerb]); + closeAndCleanupJobLocked(false /* reschedule */); + return; + } + if (mCancelled) { if (DEBUG) { - Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString()); - } - if (mVerb != VERB_BINDING) { - Slog.e(TAG, "Sending onStartJob for a job that isn't pending. " - + VERB_STRINGS[mVerb]); - closeAndCleanupJobH(false /* reschedule */); - return; - } - if (mCancelled.get()) { - if (DEBUG) { - Slog.d(TAG, "Job cancelled while waiting for bind to complete. " - + mRunningJob); - } - closeAndCleanupJobH(true /* reschedule */); - return; - } - try { - mVerb = VERB_STARTING; - scheduleOpTimeOutLocked(); - service.startJob(mParams); - } catch (Exception e) { - // We catch 'Exception' because client-app malice or bugs might induce a wide - // range of possible exception-throw outcomes from startJob() and its handling - // of the client's ParcelableBundle extras. - Slog.e(TAG, "Error sending onStart message to '" + - mRunningJob.getServiceComponent().getShortClassName() + "' ", e); + Slog.d(TAG, "Job cancelled while waiting for bind to complete. " + + mRunningJob); } + closeAndCleanupJobLocked(true /* reschedule */); + return; } + try { + mVerb = VERB_STARTING; + scheduleOpTimeOutLocked(); + service.startJob(mParams); + } catch (Exception e) { + // We catch 'Exception' because client-app malice or bugs might induce a wide + // range of possible exception-throw outcomes from startJob() and its handling + // of the client's ParcelableBundle extras. + Slog.e(TAG, "Error sending onStart message to '" + + mRunningJob.getServiceComponent().getShortClassName() + "' ", e); + } + } - /** - * State behaviours. - * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout. - * _PENDING -> Error - * _EXECUTING -> Error - * _STOPPING -> Error - */ - private void handleStartedH(boolean workOngoing) { - switch (mVerb) { - case VERB_STARTING: - mVerb = VERB_EXECUTING; - if (!workOngoing) { - // Job is finished already so fast-forward to handleFinished. - handleFinishedH(false); - return; - } - if (mCancelled.get()) { - if (DEBUG) { - Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete."); - } - // Cancelled *while* waiting for acknowledgeStartMessage from client. - handleCancelH(); - return; + /** + * State behaviours. + * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout. + * _PENDING -> Error + * _EXECUTING -> Error + * _STOPPING -> Error + */ + private void handleStartedLocked(boolean workOngoing) { + switch (mVerb) { + case VERB_STARTING: + mVerb = VERB_EXECUTING; + if (!workOngoing) { + // Job is finished already so fast-forward to handleFinished. + handleFinishedLocked(false); + return; + } + if (mCancelled) { + if (DEBUG) { + Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete."); } - scheduleOpTimeOutLocked(); - break; - default: - Slog.e(TAG, "Handling started job but job wasn't starting! Was " - + VERB_STRINGS[mVerb] + "."); + // Cancelled *while* waiting for acknowledgeStartMessage from client. + handleCancelLocked(); return; - } + } + scheduleOpTimeOutLocked(); + break; + default: + Slog.e(TAG, "Handling started job but job wasn't starting! Was " + + VERB_STRINGS[mVerb] + "."); + return; } + } - /** - * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done. - * _STOPPING -> Successful finish, clean up and notify done. - * _STARTING -> Error - * _PENDING -> Error - */ - private void handleFinishedH(boolean reschedule) { - switch (mVerb) { - case VERB_EXECUTING: - case VERB_STOPPING: - closeAndCleanupJobH(reschedule); - break; - default: - Slog.e(TAG, "Got an execution complete message for a job that wasn't being" + - "executed. Was " + VERB_STRINGS[mVerb] + "."); - } + /** + * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done. + * _STOPPING -> Successful finish, clean up and notify done. + * _STARTING -> Error + * _PENDING -> Error + */ + private void handleFinishedLocked(boolean reschedule) { + switch (mVerb) { + case VERB_EXECUTING: + case VERB_STOPPING: + closeAndCleanupJobLocked(reschedule); + break; + default: + Slog.e(TAG, "Got an execution complete message for a job that wasn't being" + + "executed. Was " + VERB_STRINGS[mVerb] + "."); } + } - /** - * A job can be in various states when a cancel request comes in: - * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for - * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)} - * _STARTING -> Mark as cancelled and wait for - * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)} - * _EXECUTING -> call {@link #sendStopMessageH}}, but only if there are no callbacks - * in the message queue. - * _ENDING -> No point in doing anything here, so we ignore. - */ - private void handleCancelH() { - if (JobSchedulerService.DEBUG) { - Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " " - + VERB_STRINGS[mVerb]); - } - switch (mVerb) { - case VERB_BINDING: - case VERB_STARTING: - mCancelled.set(true); - break; - case VERB_EXECUTING: - if (hasMessages(MSG_CALLBACK)) { - // If the client has called jobFinished, ignore this cancel. - return; - } - sendStopMessageH(); - break; - case VERB_STOPPING: - // Nada. - break; - default: - Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb); - break; - } + /** + * A job can be in various states when a cancel request comes in: + * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for + * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)} + * _STARTING -> Mark as cancelled and wait for + * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)} + * _EXECUTING -> call {@link #sendStopMessageLocked}}, but only if there are no callbacks + * in the message queue. + * _ENDING -> No point in doing anything here, so we ignore. + */ + private void handleCancelLocked() { + if (JobSchedulerService.DEBUG) { + Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " " + + VERB_STRINGS[mVerb]); } + switch (mVerb) { + case VERB_BINDING: + case VERB_STARTING: + mCancelled = true; + break; + case VERB_EXECUTING: + sendStopMessageLocked(); + break; + case VERB_STOPPING: + // Nada. + break; + default: + Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb); + break; + } + } - /** Process MSG_TIMEOUT here. */ - private void handleOpTimeoutH() { - switch (mVerb) { - case VERB_BINDING: - Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() + - ", dropping."); - closeAndCleanupJobH(false /* needsReschedule */); - break; - case VERB_STARTING: - // Client unresponsive - wedged or failed to respond in time. We don't really - // know what happened so let's log it and notify the JobScheduler - // FINISHED/NO-RETRY. - Slog.e(TAG, "No response from client for onStartJob '" + - mRunningJob.toShortString()); - closeAndCleanupJobH(false /* needsReschedule */); - break; - case VERB_STOPPING: - // At least we got somewhere, so fail but ask the JobScheduler to reschedule. - Slog.e(TAG, "No response from client for onStopJob, '" + - mRunningJob.toShortString()); - closeAndCleanupJobH(true /* needsReschedule */); - break; - case VERB_EXECUTING: - // Not an error - client ran out of time. - Slog.i(TAG, "Client timed out while executing (no jobFinished received)." + - " sending onStop. " + mRunningJob.toShortString()); - mParams.setStopReason(JobParameters.REASON_TIMEOUT); - sendStopMessageH(); - break; - default: - Slog.e(TAG, "Handling timeout for an invalid job state: " + - mRunningJob.toShortString() + ", dropping."); - closeAndCleanupJobH(false /* needsReschedule */); - } + /** Process MSG_TIMEOUT here. */ + private void handleOpTimeoutLocked() { + switch (mVerb) { + case VERB_BINDING: + Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() + + ", dropping."); + closeAndCleanupJobLocked(false /* needsReschedule */); + break; + case VERB_STARTING: + // Client unresponsive - wedged or failed to respond in time. We don't really + // know what happened so let's log it and notify the JobScheduler + // FINISHED/NO-RETRY. + Slog.e(TAG, "No response from client for onStartJob '" + + mRunningJob.toShortString()); + closeAndCleanupJobLocked(false /* needsReschedule */); + break; + case VERB_STOPPING: + // At least we got somewhere, so fail but ask the JobScheduler to reschedule. + Slog.e(TAG, "No response from client for onStopJob, '" + + mRunningJob.toShortString()); + closeAndCleanupJobLocked(true /* needsReschedule */); + break; + case VERB_EXECUTING: + // Not an error - client ran out of time. + Slog.i(TAG, "Client timed out while executing (no jobFinished received)." + + " sending onStop. " + mRunningJob.toShortString()); + mParams.setStopReason(JobParameters.REASON_TIMEOUT); + sendStopMessageLocked(); + break; + default: + Slog.e(TAG, "Handling timeout for an invalid job state: " + + mRunningJob.toShortString() + ", dropping."); + closeAndCleanupJobLocked(false /* needsReschedule */); } + } - /** - * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING -> - * VERB_STOPPING. - */ - private void sendStopMessageH() { - removeOpTimeOutLocked(); - if (mVerb != VERB_EXECUTING) { - Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob); - closeAndCleanupJobH(false /* reschedule */); - return; - } - try { - mVerb = VERB_STOPPING; - scheduleOpTimeOutLocked(); - service.stopJob(mParams); - } catch (RemoteException e) { - Slog.e(TAG, "Error sending onStopJob to client.", e); - // The job's host app apparently crashed during the job, so we should reschedule. - closeAndCleanupJobH(true /* reschedule */); - } + /** + * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING -> + * VERB_STOPPING. + */ + private void sendStopMessageLocked() { + removeOpTimeOutLocked(); + if (mVerb != VERB_EXECUTING) { + Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob); + closeAndCleanupJobLocked(false /* reschedule */); + return; + } + try { + mVerb = VERB_STOPPING; + scheduleOpTimeOutLocked(); + service.stopJob(mParams); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending onStopJob to client.", e); + // The job's host app apparently crashed during the job, so we should reschedule. + closeAndCleanupJobLocked(true /* reschedule */); } + } - /** - * The provided job has finished, either by calling - * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} - * or from acknowledging the stop message we sent. Either way, we're done tracking it and - * we want to clean up internally. - */ - private void closeAndCleanupJobH(boolean reschedule) { - final JobStatus completedJob; - synchronized (mLock) { - if (mVerb == VERB_FINISHED) { - return; - } - completedJob = mRunningJob; - mJobPackageTracker.noteInactive(completedJob); - try { - mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), - mRunningJob.getSourceUid()); - } catch (RemoteException e) { - // Whatever. - } - if (mWakeLock != null) { - mWakeLock.release(); - } - mContext.unbindService(JobServiceContext.this); - mWakeLock = null; - mRunningJob = null; - mParams = null; - mVerb = VERB_FINISHED; - mCancelled.set(false); - service = null; - mAvailable = true; - removeOpTimeOutLocked(); - removeMessages(MSG_CALLBACK); - removeMessages(MSG_SERVICE_BOUND); - removeMessages(MSG_CANCEL); - removeMessages(MSG_SHUTDOWN_EXECUTION); - mCompletedListener.onJobCompletedLocked(completedJob, reschedule); - } + /** + * The provided job has finished, either by calling + * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} + * or from acknowledging the stop message we sent. Either way, we're done tracking it and + * we want to clean up internally. + */ + private void closeAndCleanupJobLocked(boolean reschedule) { + final JobStatus completedJob; + if (mVerb == VERB_FINISHED) { + return; } + completedJob = mRunningJob; + mJobPackageTracker.noteInactive(completedJob); + try { + mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), + mRunningJob.getSourceUid()); + } catch (RemoteException e) { + // Whatever. + } + if (mWakeLock != null) { + mWakeLock.release(); + } + mContext.unbindService(JobServiceContext.this); + mWakeLock = null; + mRunningJob = null; + mParams = null; + mVerb = VERB_FINISHED; + mCancelled = false; + service = null; + mAvailable = true; + removeOpTimeOutLocked(); + mCompletedListener.onJobCompletedLocked(completedJob, reschedule); } /** diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index e8cc078b7eb1..1ab66b9abb7f 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -26,7 +26,6 @@ import android.content.ContentProvider; import android.content.Intent; import android.net.Uri; import android.os.Binder; -import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -35,6 +34,8 @@ import android.util.ArraySet; import android.util.Slog; import android.util.TimeUtils; +import com.android.server.job.GrantedUriPermissions; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -103,7 +104,7 @@ public final class JobStatus { final String tag; - private IBinder permissionOwner; + private GrantedUriPermissions uriPerms; private boolean prepared; /** @@ -284,12 +285,17 @@ public final class JobStatus { earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis); } - public void enqueueWorkLocked(JobWorkItem work) { + public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) { if (pendingWork == null) { pendingWork = new ArrayList<>(); } work.setWorkId(nextPendingWorkId); nextPendingWorkId++; + if (work.getIntent() != null + && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) { + work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid, + sourcePackageName, sourceUserId, toShortString())); + } pendingWork.add(work); } @@ -311,12 +317,20 @@ public final class JobStatus { return executingWork != null && executingWork.size() > 0; } - public boolean completeWorkLocked(int workId) { + private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) { + if (work.getGrants() != null) { + ((GrantedUriPermissions)work.getGrants()).revoke(am); + } + } + + public boolean completeWorkLocked(IActivityManager am, int workId) { if (executingWork != null) { final int N = executingWork.size(); for (int i = 0; i < N; i++) { - if (executingWork.get(i).getWorkId() == workId) { + JobWorkItem work = executingWork.get(i); + if (work.getWorkId() == workId) { executingWork.remove(i); + ungrantWorkItem(am, work); return true; } } @@ -324,15 +338,36 @@ public final class JobStatus { return false; } - public void stopTrackingJobLocked(JobStatus incomingJob) { + private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) { + if (list != null) { + final int N = list.size(); + for (int i = 0; i < N; i++) { + ungrantWorkItem(am, list.get(i)); + } + } + } + + public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) { if (incomingJob != null) { - // We are replacing with a new job -- transfer the work! - incomingJob.pendingWork = pendingWork; + // We are replacing with a new job -- transfer the work! We do any executing + // work first, since that was originally at the front of the pending work. + if (executingWork != null && executingWork.size() > 0) { + incomingJob.pendingWork = executingWork; + } + if (incomingJob.pendingWork == null) { + incomingJob.pendingWork = pendingWork; + } else if (pendingWork != null && pendingWork.size() > 0) { + incomingJob.pendingWork.addAll(pendingWork); + } pendingWork = null; + executingWork = null; incomingJob.nextPendingWorkId = nextPendingWorkId; } else { // We are completely stopping the job... need to clean up work. - // XXX remove perms when that is impl. + ungrantWorkList(am, pendingWork); + pendingWork = null; + ungrantWorkList(am, executingWork); + executingWork = null; } } @@ -344,10 +379,8 @@ public final class JobStatus { prepared = true; final ClipData clip = job.getClipData(); if (clip != null) { - final int N = clip.getItemCount(); - for (int i = 0; i < N; i++) { - grantItemLocked(am, clip.getItemAt(i), sourceUid, sourcePackageName, sourceUserId); - } + uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName, + sourceUserId, job.getClipGrantFlags(), toShortString()); } } @@ -357,14 +390,9 @@ public final class JobStatus { return; } prepared = false; - if (permissionOwner != null) { - final ClipData clip = job.getClipData(); - if (clip != null) { - final int N = clip.getItemCount(); - for (int i = 0; i < N; i++) { - revokeItemLocked(am, clip.getItemAt(i)); - } - } + if (uriPerms != null) { + uriPerms.revoke(am); + uriPerms = null; } } @@ -372,57 +400,6 @@ public final class JobStatus { return prepared; } - private final void grantUriLocked(IActivityManager am, Uri uri, int sourceUid, - String targetPackage, int targetUserId) { - try { - int sourceUserId = ContentProvider.getUserIdFromUri(uri, - UserHandle.getUserId(sourceUid)); - uri = ContentProvider.getUriWithoutUserId(uri); - if (permissionOwner == null) { - permissionOwner = am.newUriPermissionOwner("job: " + toShortString()); - } - am.grantUriPermissionFromOwner(permissionOwner, sourceUid, targetPackage, - uri, job.getClipGrantFlags(), sourceUserId, targetUserId); - } catch (RemoteException e) { - Slog.e("JobScheduler", "AM dead"); - } - } - - private final void grantItemLocked(IActivityManager am, ClipData.Item item, int sourceUid, - String targetPackage, int targetUserId) { - if (item.getUri() != null) { - grantUriLocked(am, item.getUri(), sourceUid, targetPackage, targetUserId); - } - Intent intent = item.getIntent(); - if (intent != null && intent.getData() != null) { - grantUriLocked(am, intent.getData(), sourceUid, targetPackage, targetUserId); - } - } - - private final void revokeUriLocked(IActivityManager am, Uri uri) { - int userId = ContentProvider.getUserIdFromUri(uri, - UserHandle.getUserId(Binder.getCallingUid())); - long ident = Binder.clearCallingIdentity(); - try { - uri = ContentProvider.getUriWithoutUserId(uri); - am.revokeUriPermissionFromOwner(permissionOwner, uri, - job.getClipGrantFlags(), userId); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private final void revokeItemLocked(IActivityManager am, ClipData.Item item) { - if (item.getUri() != null) { - revokeUriLocked(am, item.getUri()); - } - Intent intent = item.getIntent(); - if (intent != null && intent.getData() != null) { - revokeUriLocked(am, intent.getData()); - } - } - public JobInfo getJob() { return job; } @@ -833,6 +810,15 @@ public final class JobStatus { } } + private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) { + pw.print(prefix); pw.print(" #"); pw.print(index); pw.print(": #"); + pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent()); + if (work.getGrants() != null) { + pw.print(prefix); pw.println(" URI grants:"); + ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + " "); + } + } + // Dumpsys infrastructure public void dump(PrintWriter pw, String prefix, boolean full) { pw.print(prefix); UserHandle.formatUid(pw, callingUid); @@ -898,6 +884,10 @@ public final class JobStatus { job.getClipData().toShortString(b); pw.println(b); } + if (uriPerms != null) { + pw.print(prefix); pw.println(" Granted URI permissions:"); + uriPerms.dump(pw, prefix + " "); + } if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType()); } @@ -950,17 +940,13 @@ public final class JobStatus { if (pendingWork != null && pendingWork.size() > 0) { pw.print(prefix); pw.println("Pending work:"); for (int i = 0; i < pendingWork.size(); i++) { - JobWorkItem work = pendingWork.get(i); - pw.print(prefix); pw.print(" #"); pw.print(i); pw.print(": #"); - pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent()); + dumpJobWorkItem(pw, prefix, pendingWork.get(i), i); } } if (executingWork != null && executingWork.size() > 0) { pw.print(prefix); pw.println("Executing work:"); for (int i = 0; i < executingWork.size(); i++) { - JobWorkItem work = executingWork.get(i); - pw.print(prefix); pw.print(" #"); pw.print(i); pw.print(": #"); - pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent()); + dumpJobWorkItem(pw, prefix, executingWork.get(i), i); } } pw.print(prefix); pw.print("Earliest run time: "); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 3a1ddd789f13..91c32e48d802 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -288,6 +288,9 @@ public class GnssLocationProvider implements LocationProviderInterface { // current setting - 4 hours private static final long MAX_RETRY_INTERVAL = 4*60*60*1000; + // Timeout when holding wakelocks for downloading XTRA data. + private static final long DOWNLOAD_XTRA_DATA_TIMEOUT_MS = 60 * 1000; + private BackOff mNtpBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL); private BackOff mXtraBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL); @@ -575,6 +578,10 @@ public class GnssLocationProvider implements LocationProviderInterface { // override default value of this if lpp_prof is not empty properties.setProperty("LPP_PROFILE", lpp_prof); } + /* + * Overlay carrier properties from a debug configuration file. + */ + loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties); // TODO: we should get rid of C2K specific setting. setSuplHostPort(properties.getProperty("SUPL_HOST"), properties.getProperty("SUPL_PORT")); @@ -587,10 +594,6 @@ public class GnssLocationProvider implements LocationProviderInterface { Log.e(TAG, "unable to parse C2K_PORT: " + portString); } } - /* - * Allow carrier properties to be loaded from a debug configuration file. - */ - loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties); if (native_is_gnss_configuration_supported()) { Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() { { @@ -664,7 +667,7 @@ public class GnssLocationProvider implements LocationProviderInterface { } } catch (IOException e) { - Log.v(TAG, "Could not open GPS configuration file " + filename); + if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename); return false; } return true; @@ -986,7 +989,7 @@ public class GnssLocationProvider implements LocationProviderInterface { mDownloadXtraDataPending = STATE_DOWNLOADING; // hold wake lock while task runs - mWakeLock.acquire(); + mWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS); Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()"); AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { @Override @@ -1009,7 +1012,11 @@ public class GnssLocationProvider implements LocationProviderInterface { } // release wake lock held by task - mWakeLock.release(); + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } else { + Log.e(TAG, "WakeLock expired before release in handleDownloadXtraData()"); + } Log.i(TAG, "WakeLock released by handleDownloadXtraData()"); } }); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 53a8092fa558..a275f49582c6 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -101,8 +101,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private ParceledListSlice mQueue; private CharSequence mQueueTitle; private int mRatingType; - private int mRepeatMode; - private boolean mShuffleModeEnabled; // End TransportPerformer fields // Volume handling fields @@ -622,47 +620,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } - private void pushRepeatModeUpdate() { - synchronized (mLock) { - if (mDestroyed) { - return; - } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onRepeatModeChanged(mRepeatMode); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removed dead callback in pushRepeatModeUpdate", - holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushRepeatModeUpdate", holder, e); - } - } - } - } - - private void pushShuffleModeUpdate() { - synchronized (mLock) { - if (mDestroyed) { - return; - } - for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { - ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); - try { - holder.mCallback.onShuffleModeChanged(mShuffleModeEnabled); - } catch (DeadObjectException e) { - mControllerCallbackHolders.remove(i); - logCallbackException("Removed dead callback in pushShuffleModeUpdate", - holder, e); - } catch (RemoteException e) { - logCallbackException("unexpected exception in pushShuffleModeUpdate", - holder, e); - } - } - } - } - private void pushSessionDestroyed() { synchronized (mLock) { // This is the only method that may be (and can only be) called @@ -794,7 +751,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public void setActive(boolean active) { mIsActive = active; - mService.updateSession(MediaSessionRecord.this); + final long token = Binder.clearCallingIdentity(); + try { + mService.updateSession(MediaSessionRecord.this); + } finally { + Binder.restoreCallingIdentity(token); + } mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); } @@ -887,30 +849,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setRepeatMode(int repeatMode) { - boolean changed; - synchronized (mLock) { - changed = mRepeatMode != repeatMode; - mRepeatMode = repeatMode; - } - if (changed) { - mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE); - } - } - - @Override - public void setShuffleModeEnabled(boolean enabled) { - boolean changed; - synchronized (mLock) { - changed = mShuffleModeEnabled != enabled; - mShuffleModeEnabled = enabled; - } - if (changed) { - mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE); - } - } - - @Override public void setCurrentVolume(int volume) { mCurrentVolume = volume; mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); @@ -929,7 +867,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } if (typeChanged) { - mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); + final long token = Binder.clearCallingIdentity(); + try { + mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); + } finally { + Binder.restoreCallingIdentity(token); + } mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); } } @@ -944,7 +887,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mMaxVolume = max; } if (typeChanged) { - mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); + final long token = Binder.clearCallingIdentity(); + try { + mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); + } finally { + Binder.restoreCallingIdentity(token); + } mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); } } @@ -1126,54 +1074,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } - public void repeatMode(int repeatMode) { - try { - mCb.onRepeatMode(repeatMode); - } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in repeatMode.", e); - } - } - - public void shuffleMode(boolean enabled) { - try { - mCb.onShuffleMode(enabled); - } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in shuffleMode.", e); - } - } - - public void addQueueItem(MediaDescription description) { - try { - mCb.onAddQueueItem(description); - } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in addQueueItem.", e); - } - } - - public void addQueueItemAt(MediaDescription description, int index) { - try { - mCb.onAddQueueItemAt(description, index); - } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in addQueueItemAt.", e); - } - } - - public void removeQueueItem(MediaDescription description) { - try { - mCb.onRemoveQueueItem(description); - } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in removeQueueItem.", e); - } - } - - public void removeQueueItemAt(int index) { - try { - mCb.onRemoveQueueItemAt(index); - } catch (RemoteException e) { - Slog.e(TAG, "Remote failure in removeQueueItem.", e); - } - } - public void adjustVolume(int direction) { try { mCb.onAdjustVolume(direction); @@ -1410,25 +1310,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void repeatMode(int repeatMode) { - updateCallingPackage(); - mSessionCb.repeatMode(repeatMode); - } - - @Override - public void shuffleMode(boolean enabled) throws RemoteException { - updateCallingPackage(); - mSessionCb.shuffleMode(enabled); - } - - - @Override public void sendCustomAction(String action, Bundle args) throws RemoteException { updateCallingPackage(); mSessionCb.sendCustomAction(action, args); } + @Override public MediaMetadata getMetadata() { synchronized (mLock) { @@ -1449,30 +1337,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void addQueueItem(MediaDescription description) { - updateCallingPackage(); - mSessionCb.addQueueItem(description); - } - - @Override - public void addQueueItemAt(MediaDescription description, int index) { - updateCallingPackage(); - mSessionCb.addQueueItemAt(description, index); - } - - @Override - public void removeQueueItem(MediaDescription description) { - updateCallingPackage(); - mSessionCb.removeQueueItem(description); - } - - @Override - public void removeQueueItemAt(int index) { - updateCallingPackage(); - mSessionCb.removeQueueItemAt(index); - } - - @Override public CharSequence getQueueTitle() { return mQueueTitle; } @@ -1490,16 +1354,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public int getRepeatMode() { - return mRepeatMode; - } - - @Override - public boolean isShuffleModeEnabled() { - return mShuffleModeEnabled; - } - - @Override public boolean isTransportControlEnabled() { return MediaSessionRecord.this.isTransportControlEnabled(); } @@ -1524,9 +1378,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private static final int MSG_SEND_EVENT = 6; private static final int MSG_UPDATE_SESSION_STATE = 7; private static final int MSG_UPDATE_VOLUME = 8; - private static final int MSG_UPDATE_REPEAT_MODE = 9; - private static final int MSG_UPDATE_SHUFFLE_MODE = 10; - private static final int MSG_DESTROYED = 11; + private static final int MSG_DESTROYED = 9; public MessageHandler(Looper looper) { super(looper); @@ -1558,12 +1410,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { case MSG_UPDATE_VOLUME: pushVolumeUpdate(); break; - case MSG_UPDATE_REPEAT_MODE: - pushRepeatModeUpdate(); - break; - case MSG_UPDATE_SHUFFLE_MODE: - pushShuffleModeUpdate(); - break; case MSG_DESTROYED: pushSessionDestroyed(); } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 2f82915a459b..0e69bca9dfd1 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -121,9 +121,12 @@ class MediaSessionStack { public void removeSession(MediaSessionRecord record) { mSessions.remove(record); if (mMediaButtonSession == record) { - // When the media button session is gone, try to find the alternative media session - // in the media button session app. - onMediaSessionChangeInMediaButtonSessionApp(); + // When the media button session is removed, nullify the media button session and do not + // search for the alternative media session within the app. It's because the alternative + // media session might be a dummy which isn't able to handle the media key events. + mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged( + mMediaButtonSession, null); + mMediaButtonSession = null; } clearCache(record.getUserId()); } @@ -157,7 +160,13 @@ class MediaSessionStack { // In that case, we pick the media session whose PlaybackState matches // the audio playback configuration. if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) { - onMediaSessionChangeInMediaButtonSessionApp(); + MediaSessionRecord newMediaButtonSession = + findMediaButtonSession(mMediaButtonSession.getUid()); + if (newMediaButtonSession != mMediaButtonSession) { + mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged( + mMediaButtonSession, newMediaButtonSession); + mMediaButtonSession = newMediaButtonSession; + } } } @@ -200,22 +209,6 @@ class MediaSessionStack { } /** - * Handle the change in a media session in the media button session app. - * <p>If the app has multiple media sessions, change in a media sesion in the app may change - * the media button session. - * @see #findMediaButtonSession - */ - private void onMediaSessionChangeInMediaButtonSessionApp() { - MediaSessionRecord newMediaButtonSession = - findMediaButtonSession(mMediaButtonSession.getUid()); - if (newMediaButtonSession != mMediaButtonSession) { - mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(mMediaButtonSession, - newMediaButtonSession); - mMediaButtonSession = newMediaButtonSession; - } - } - - /** * Find the media button session with the given {@param uid}. * If the app has multiple media sessions, the media session matches the audio playback state * becomes the media button session. diff --git a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java index ce976d29216b..acedafc4025c 100644 --- a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java +++ b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java @@ -15,19 +15,25 @@ */ package com.android.server.notification; +import android.util.Slog; + import java.util.Comparator; /** * Sorts notifications by their global sort key. */ public class GlobalSortKeyComparator implements Comparator<NotificationRecord> { + private final static String TAG = "GlobalSortComp"; + @Override public int compare(NotificationRecord left, NotificationRecord right) { if (left.getGlobalSortKey() == null) { - throw new IllegalStateException("Missing left global sort key: " + left); + Slog.wtf(TAG, "Missing left global sort key: " + left); + return 1; } if (right.getGlobalSortKey() == null) { - throw new IllegalStateException("Missing right global sort key: " + right); + Slog.wtf(TAG, "Missing right global sort key: " + right); + return -1; } return left.getGlobalSortKey().compareTo(right.getGlobalSortKey()); } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 90e9b927a4c7..3cb2f35d1585 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -356,7 +356,6 @@ abstract public class ManagedServices { private void rebuildRestoredPackages() { mRestoredPackages.clear(); - mSnoozingForCurrentProfiles.clear(); String secureSettingName = restoredSettingName(mConfig.secureSettingName); String secondarySettingName = mConfig.secondarySettingName == null ? null : restoredSettingName(mConfig.secondarySettingName); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f334ba41bdf1..c2892049e64d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -97,6 +97,7 @@ import android.media.AudioManagerInternal; import android.media.IRingtonePlayer; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -1322,7 +1323,8 @@ public class NotificationManagerService extends SystemService { if (!fromListener) { mListeners.notifyNotificationChannelChanged( - pkg, modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); + pkg, UserHandle.getUserHandleForUid(uid), + modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); } synchronized (mNotificationLock) { @@ -1647,7 +1649,8 @@ public class NotificationManagerService extends SystemService { Preconditions.checkNotNull(group, "group in list is null"); mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true /* fromTargetApp */); - mListeners.notifyNotificationChannelGroupChanged(pkg, group, + mListeners.notifyNotificationChannelGroupChanged(pkg, + UserHandle.of(UserHandle.getCallingUserId()), group, NOTIFICATION_CHANNEL_OR_GROUP_ADDED); } savePolicyFile(); @@ -1663,6 +1666,7 @@ public class NotificationManagerService extends SystemService { mRankingHelper.createNotificationChannel(pkg, uid, channel, true /* fromTargetApp */); mListeners.notifyNotificationChannelChanged(pkg, + UserHandle.getUserHandleForUid(uid), mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); } @@ -1708,6 +1712,7 @@ public class NotificationManagerService extends SystemService { UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null); mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId); mListeners.notifyNotificationChannelChanged(pkg, + UserHandle.getUserHandleForUid(callingUid), mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true), NOTIFICATION_CHANNEL_OR_GROUP_DELETED); savePolicyFile(); @@ -1737,11 +1742,14 @@ public class NotificationManagerService extends SystemService { true, UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null); - mListeners.notifyNotificationChannelChanged(pkg, deletedChannel, + mListeners.notifyNotificationChannelChanged(pkg, + UserHandle.getUserHandleForUid(callingUid), + deletedChannel, NOTIFICATION_CHANNEL_OR_GROUP_DELETED); } mListeners.notifyNotificationChannelGroupChanged( - pkg, groupToDelete, NOTIFICATION_CHANNEL_OR_GROUP_DELETED); + pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete, + NOTIFICATION_CHANNEL_OR_GROUP_DELETED); savePolicyFile(); } } @@ -1866,10 +1874,9 @@ public class NotificationManagerService extends SystemService { int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), incomingUserId, true, false, "getAppActiveNotifications", pkg); - final ArrayMap<String, StatusBarNotification> map - = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size()); - synchronized (mNotificationLock) { + final ArrayMap<String, StatusBarNotification> map + = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size()); final int N = mNotificationList.size(); for (int i = 0; i < N; i++) { StatusBarNotification sbn = sanitizeSbn(pkg, userId, @@ -1892,11 +1899,10 @@ public class NotificationManagerService extends SystemService { map.put(sbn.getKey(), sbn); // pending update overwrites existing post here } } + final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size()); + list.addAll(map.values()); + return new ParceledListSlice<StatusBarNotification>(list); } - - final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size()); - list.addAll(map.values()); - return new ParceledListSlice<StatusBarNotification>(list); } private StatusBarNotification sanitizeSbn(String pkg, int userId, @@ -2028,8 +2034,10 @@ public class NotificationManagerService extends SystemService { long identity = Binder.clearCallingIdentity(); try { // allow bound services to disable themselves - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - info.getOwner().setComponentState(info.component, false); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + info.getOwner().setComponentState(info.component, false); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2093,8 +2101,10 @@ public class NotificationManagerService extends SystemService { String key, String snoozeCriterionId) { long identity = Binder.clearCallingIdentity(); try { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2110,8 +2120,10 @@ public class NotificationManagerService extends SystemService { long duration) { long identity = Binder.clearCallingIdentity(); try { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - snoozeNotificationInt(key, duration, null, info); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + snoozeNotificationInt(key, duration, null, info); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2126,9 +2138,11 @@ public class NotificationManagerService extends SystemService { public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) { long identity = Binder.clearCallingIdentity(); try { - final ManagedServiceInfo info = - mNotificationAssistants.checkServiceTokenLocked(token); - unsnoozeNotificationInt(key, info); + synchronized (mNotificationLock) { + final ManagedServiceInfo info = + mNotificationAssistants.checkServiceTokenLocked(token); + unsnoozeNotificationInt(key, info); + } } finally { Binder.restoreCallingIdentity(identity); } @@ -2691,43 +2705,62 @@ public class NotificationManagerService extends SystemService { @Override public void updateNotificationChannelFromPrivilegedListener(INotificationListener token, - String pkg, NotificationChannel channel) throws RemoteException { + String pkg, UserHandle user, NotificationChannel channel) throws RemoteException { Preconditions.checkNotNull(channel); + Preconditions.checkNotNull(pkg); + Preconditions.checkNotNull(user); - ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - if (!hasCompanionDevice(info)) { - throw new SecurityException(info + " does not have access"); - } - - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - updateNotificationChannelInt(pkg, uid, channel, true); + verifyPrivilegedListener(token, user); + updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true); } @Override public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener( - INotificationListener token, String pkg) throws RemoteException { - ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - if (!hasCompanionDevice(info)) { - throw new SecurityException(info + " does not have access"); - } + INotificationListener token, String pkg, UserHandle user) throws RemoteException { + Preconditions.checkNotNull(pkg); + Preconditions.checkNotNull(user); + verifyPrivilegedListener(token, user); - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */); + return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user), + false /* includeDeleted */); } @Override public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsFromPrivilegedListener( - INotificationListener token, String pkg) throws RemoteException { - ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + INotificationListener token, String pkg, UserHandle user) throws RemoteException { + Preconditions.checkNotNull(pkg); + Preconditions.checkNotNull(user); + verifyPrivilegedListener(token, user); + + List<NotificationChannelGroup> groups = new ArrayList<>(); + groups.addAll(mRankingHelper.getNotificationChannelGroups( + pkg, getUidForPackageAndUser(pkg, user))); + return new ParceledListSlice<>(groups); + } + + private void verifyPrivilegedListener(INotificationListener token, UserHandle user) { + ManagedServiceInfo info; + synchronized (mNotificationLock) { + info = mListeners.checkServiceTokenLocked(token); + } if (!hasCompanionDevice(info)) { throw new SecurityException(info + " does not have access"); } + if (!info.enabledAndUserMatches(user.getIdentifier())) { + throw new SecurityException(info + " does not have access"); + } + } - List<NotificationChannelGroup> groups = new ArrayList<>(); - int uid = mPackageManager.getPackageUid(pkg, 0, info.userid); - groups.addAll(mRankingHelper.getNotificationChannelGroups(pkg, uid)); - return new ParceledListSlice<>(groups); + private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException { + int uid = 0; + long identity = Binder.clearCallingIdentity(); + try { + uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier()); + } finally { + Binder.restoreCallingIdentity(identity); + } + return uid; } }; @@ -3075,8 +3108,10 @@ public class NotificationManagerService extends SystemService { @Override public void run() { synchronized (mNotificationLock) { - removeForegroundServiceFlagByListLocked(mEnqueuedNotifications, pkg, notificationId, userId); - removeForegroundServiceFlagByListLocked(mNotificationList, pkg, notificationId, userId); + removeForegroundServiceFlagByListLocked( + mEnqueuedNotifications, pkg, notificationId, userId); + removeForegroundServiceFlagByListLocked( + mNotificationList, pkg, notificationId, userId); } } }); @@ -3162,16 +3197,18 @@ public class NotificationManagerService extends SystemService { // STOPSHIP TODO: should throw instead of logging or toasting. // throw new IllegalArgumentException(noChannelStr); Log.e(TAG, noChannelStr); - - final String noChannelToastStr = - "Developer warning for package \"" + pkg + "\"\n" + + doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" + "Failed to post notification on channel \"" + channelId + "\"\n" + - "See log for more details"; - Toast noChannelToast = - Toast.makeText(getContext(), noChannelToastStr, Toast.LENGTH_LONG); - noChannelToast.show(); + "See log for more details"); return; + } else if (channelId == null && shouldWarnUseChannels(pkg, notificationUid)) { + // STOPSHIP TODO: remove once default channel is removed for all apps that target O. + Log.e(TAG, "Developer Warning for package " + pkg + + ", no channel specified for posted notification: " + notification); + doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" + + "Posted notification should specify a channel"); } + final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, notificationUid, callingPid, notification, user, null, System.currentTimeMillis()); @@ -3203,6 +3240,30 @@ public class NotificationManagerService extends SystemService { idOut[0] = id; } + private void doDebugOnlyToast(CharSequence toastText) { + if (Build.IS_DEBUGGABLE) { + try { + Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG); + toast.show(); + } catch (RuntimeException e) { + Slog.w(TAG, "Unable to toast with text: " + toastText, e); + } + } + } + + // STOPSHIP - Remove once RankingHelper deletes default channel for all apps targeting O. + private boolean shouldWarnUseChannels(String pkg, int uid) { + try { + final int userId = UserHandle.getUserId(uid); + final ApplicationInfo applicationInfo = + mPackageManagerClient.getApplicationInfoAsUser(pkg, 0, userId); + return applicationInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1; + } catch (NameNotFoundException e) { + Slog.e(TAG, e.toString()); + return false; + } + } + private int resolveNotificationUid(String opPackageName, int callingUid, int userId) { // The system can post notifications on behalf of any package it wants if (isCallerSystem() && opPackageName != null && !"android".equals(opPackageName)) { @@ -3552,7 +3613,7 @@ public class NotificationManagerService extends SystemService { final boolean aboveThreshold = record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT; final boolean canInterrupt = aboveThreshold && !record.isIntercepted(); - if (DBG || record.isIntercepted()) + if (DBG) Slog.v(TAG, "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + " intercept=" + record.isIntercepted() @@ -5019,7 +5080,7 @@ public class NotificationManagerService extends SystemService { } } - protected void notifyNotificationChannelChanged(final String pkg, + protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user, final NotificationChannel channel, final int modificationType) { if (channel == null) { return; @@ -5034,15 +5095,16 @@ public class NotificationManagerService extends SystemService { mHandler.post(new Runnable() { @Override public void run() { - notifyNotificationChannelChanged(serviceInfo, pkg, channel, - modificationType); + notifyNotificationChannelChanged( + serviceInfo, pkg, user, channel, modificationType); } }); } } - protected void notifyNotificationChannelGroupChanged(final String pkg, - final NotificationChannelGroup group, final int modificationType) { + protected void notifyNotificationChannelGroupChanged( + final String pkg, final UserHandle user, final NotificationChannelGroup group, + final int modificationType) { if (group == null) { return; } @@ -5056,8 +5118,8 @@ public class NotificationManagerService extends SystemService { mHandler.post(new Runnable() { @Override public void run() { - notifyNotificationChannelGroupChanged(serviceInfo, pkg, group, - modificationType); + notifyNotificationChannelGroupChanged( + serviceInfo, pkg, user, group, modificationType); } }); } @@ -5118,22 +5180,22 @@ public class NotificationManagerService extends SystemService { } void notifyNotificationChannelChanged(ManagedServiceInfo info, - final String pkg, final NotificationChannel channel, + final String pkg, final UserHandle user, final NotificationChannel channel, final int modificationType) { final INotificationListener listener = (INotificationListener) info.service; try { - listener.onNotificationChannelModification(pkg, channel, modificationType); + listener.onNotificationChannelModification(pkg, user, channel, modificationType); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex); } } private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info, - final String pkg, final NotificationChannelGroup group, + final String pkg, final UserHandle user, final NotificationChannelGroup group, final int modificationType) { final INotificationListener listener = (INotificationListener) info.service; try { - listener.onNotificationChannelGroupModification(pkg, group, modificationType); + listener.onNotificationChannelGroupModification(pkg, user, group, modificationType); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex); } diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index e472928e4ab4..21d78ee3a07a 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -231,7 +231,7 @@ final class EphemeralResolverConnection implements DeathRecipient { private final IRemoteCallback mCallback; public GetEphemeralResolveInfoCaller() { - super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); + super(BIND_SERVICE_TIMEOUT_MS); mCallback = new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) throws RemoteException { diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 89a303d21413..fc9e0a3edd51 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -35,6 +35,7 @@ import android.os.Message; import android.provider.Settings; import android.util.ArrayMap; import android.util.AtomicFile; +import android.util.ByteStringUtils; import android.util.PackageUtils; import android.util.Slog; import android.util.SparseArray; @@ -56,8 +57,10 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.function.Predicate; @@ -81,6 +84,7 @@ class InstantAppRegistry { private static final String INSTANT_APP_COOKIE_FILE_PREFIX = "cookie_"; private static final String INSTANT_APP_COOKIE_FILE_SIFFIX = ".dat"; private static final String INSTANT_APP_METADATA_FILE = "metadata.xml"; + private static final String INSTANT_APP_ANDROID_ID_FILE = "android_id"; private static final String TAG_PACKAGE = "package"; private static final String TAG_PERMISSIONS = "permissions"; @@ -142,7 +146,7 @@ class InstantAppRegistry { @Nullable byte[] cookie, @UserIdInt int userId) { if (cookie != null && cookie.length > 0) { final int maxCookieSize = mService.mContext.getPackageManager() - .getInstantAppCookieMaxSize(); + .getInstantAppCookieMaxBytes(); if (cookie.length > maxCookieSize) { Slog.e(LOG_TAG, "Instant app cookie for package " + packageName + " size " + cookie.length + " bytes while max size is " + maxCookieSize); @@ -195,6 +199,36 @@ class InstantAppRegistry { return null; } + public String getInstantAppAndroidIdLPw(@NonNull String packageName, + @UserIdInt int userId) { + File idFile = new File(getInstantApplicationDir(packageName, userId), + INSTANT_APP_ANDROID_ID_FILE); + if (idFile.exists()) { + try { + return IoUtils.readFileAsString(idFile.getAbsolutePath()); + } catch (IOException e) { + Slog.e(LOG_TAG, "Failed to read instant app android id file: " + idFile, e); + } + } + return generateInstantAppAndroidIdLPw(packageName, userId); + } + + private String generateInstantAppAndroidIdLPw(@NonNull String packageName, + @UserIdInt int userId) { + byte[] randomBytes = new byte[8]; + new SecureRandom().nextBytes(randomBytes); + String id = ByteStringUtils.toHexString(randomBytes).toLowerCase(Locale.US); + File idFile = new File(getInstantApplicationDir(packageName, userId), + INSTANT_APP_ANDROID_ID_FILE); + try (FileOutputStream fos = new FileOutputStream(idFile)) { + fos.write(id.getBytes()); + } catch (IOException e) { + Slog.e(LOG_TAG, "Error writing instant app android id file: " + idFile, e); + } + return id; + + } + public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) { List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId); List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId); @@ -462,6 +496,7 @@ class InstantAppRegistry { File instantAppDir = getInstantApplicationDir(packageName, userId); new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete(); new File(instantAppDir, INSTANT_APP_ICON_FILE).delete(); + new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).delete(); File cookie = peekInstantCookieFile(packageName, userId); if (cookie != null) { cookie.delete(); diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 7b865429d0d7..2e4b49add05f 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -274,23 +274,56 @@ public class OtaDexoptService extends IOtaDexopt.Stub { // Intercept and collect dexopt requests final List<String> commands = new ArrayList<String>(); final Installer collectingInstaller = new Installer(mContext, true) { + /** + * Encode the dexopt command into a string. + * + * Note: If you have to change the signature of this function, increase the version + * number, and update the counterpart in + * frameworks/native/cmds/installd/otapreopt.cpp. + */ @Override public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet, int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException { - commands.add(buildCommand("dexopt", - apkPath, - uid, - pkgName, - instructionSet, - dexoptNeeded, - outputPath, - dexFlags, - compilerFilter, - volumeUuid, - sharedLibraries, - seInfo)); + final StringBuilder builder = new StringBuilder(); + + // The version. Right now it's 2. + builder.append("2 "); + + builder.append("dexopt"); + + encodeParameter(builder, apkPath); + encodeParameter(builder, uid); + encodeParameter(builder, pkgName); + encodeParameter(builder, instructionSet); + encodeParameter(builder, dexoptNeeded); + encodeParameter(builder, outputPath); + encodeParameter(builder, dexFlags); + encodeParameter(builder, compilerFilter); + encodeParameter(builder, volumeUuid); + encodeParameter(builder, sharedLibraries); + encodeParameter(builder, seInfo); + + commands.add(builder.toString()); + } + + /** + * Encode a parameter as necessary for the commands string. + */ + private void encodeParameter(StringBuilder builder, Object arg) { + builder.append(' '); + + if (arg == null) { + builder.append('!'); + } + + String txt = String.valueOf(arg); + if (txt.indexOf('\0') != -1 || txt.indexOf(' ') != -1 || "!".equals(txt)) { + throw new IllegalArgumentException( + "Invalid argument while executing " + arg); + } + builder.append(txt); } }; @@ -430,28 +463,4 @@ public class OtaDexoptService extends IOtaDexopt.Stub { super(installer, installLock, context, "*otadexopt*"); } } - - /** - * Cook up argument list in the format that {@code installd} expects. - */ - private static String buildCommand(Object... args) { - final StringBuilder builder = new StringBuilder(); - for (Object arg : args) { - String escaped; - if (arg == null) { - escaped = ""; - } else { - escaped = String.valueOf(arg); - } - if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) { - throw new IllegalArgumentException( - "Invalid argument while executing " + Arrays.toString(args)); - } - if (TextUtils.isEmpty(escaped)) { - escaped = "!"; - } - builder.append(' ').append(escaped); - } - return builder.toString(); - } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 9039647cbffd..8413491aaef8 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -149,13 +149,14 @@ public class PackageDexOptimizer { final boolean profileUpdated = checkForProfileUpdates && isProfileUpdated(pkg, sharedGid, compilerFilter); - // TODO(calin,jeffhao): shared library paths should be adjusted to include previous code - // paths (b/34169257). - final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries); + String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries); // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. final int dexoptFlags = getDexFlags(pkg, compilerFilter); int result = DEX_OPT_SKIPPED; + // TODO: Iterate based on dependency hierarchy (currently alphabetically by name) + // (b/37480811). + String basePathCheck = null; for (String path : paths) { for (String dexCodeIsa : dexCodeInstructionSets) { int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated, @@ -167,6 +168,22 @@ public class PackageDexOptimizer { if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) { result = newResult; } + // Add the relative path of code we just compiled to the shared libraries. + int slashIndex = path.lastIndexOf('/') + 1; + String relativePath = path.substring(slashIndex); + if (sharedLibrariesPath == null) { + sharedLibrariesPath = relativePath; + } else { + sharedLibrariesPath += ":" + relativePath; + } + // Sanity check that the base paths are all the same. + String basePath = path.substring(0, slashIndex); + if (basePathCheck == null) { + basePathCheck = basePath; + } else if (!basePath.equals(basePathCheck)) { + Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " + + basePathCheck); + } } } return result; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e79f46b4f3be..4d026e3d0f89 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -364,7 +364,8 @@ import java.util.concurrent.atomic.AtomicInteger; * $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases * </pre> */ -public class PackageManagerService extends IPackageManager.Stub { +public class PackageManagerService extends IPackageManager.Stub + implements PackageSender { static final String TAG = "PackageManager"; static final boolean DEBUG_SETTINGS = false; static final boolean DEBUG_PREFERRED = false; @@ -954,6 +955,11 @@ public class PackageManagerService extends IPackageManager.Stub { verificationIntent.setComponent(mIntentFilterVerifierComponent); verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + DeviceIdleController.LocalService idleController = getDeviceIdleController(); + idleController.addPowerSaveTempWhitelistApp(Process.myUid(), + mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(), + userId, false, "intent filter verifier"); + UserHandle user = new UserHandle(userId); mContext.sendBroadcastAsUser(verificationIntent, user); if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, @@ -1833,10 +1839,6 @@ public class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, res.uid); if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); - } else { - sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_ADDED, packageName, - extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null /*targetPackage*/, null /*finishedReceiver*/, updateUsers); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, @@ -2804,8 +2806,12 @@ public class PackageManagerService extends IPackageManager.Stub { mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(); - mIntentFilterVerifier = new IntentVerifierProxy(mContext, - mIntentFilterVerifierComponent); + if (mIntentFilterVerifierComponent != null) { + mIntentFilterVerifier = new IntentVerifierProxy(mContext, + mIntentFilterVerifierComponent); + } else { + mIntentFilterVerifier = null; + } mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES, SharedLibraryInfo.VERSION_UNDEFINED); @@ -3045,9 +3051,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (best != null) { return best.getComponentInfo().getComponentName(); - } else { - throw new RuntimeException("There must be at least one intent filter verifier"); } + Slog.w(TAG, "Intent filter verifier not found"); + return null; } private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() { @@ -4217,8 +4223,10 @@ public class PackageManagerService extends IPackageManager.Stub { } SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(), - libInfo.getVersion(), libInfo.getType(), libInfo.getDeclaringPackage(), - getPackagesUsingSharedLibraryLPr(libInfo, flags, userId)); + // TODO: Remove cast for lib version once internally we support longs. + (int) libInfo.getVersion(), libInfo.getType(), + libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo, + flags, userId)); if (result == null) { result = new ArrayList<>(); @@ -13083,7 +13091,7 @@ public class PackageManagerService extends IPackageManager.Stub { } }; - final void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, + public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, final int[] userIds) { mHandler.post(new Runnable() { @@ -13201,7 +13209,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (dcsUid > 0) { am.backgroundWhitelistUid(dcsUid); } - am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(), + am.startService(null, intent, null, false, mContext.getOpPackageName(), UserHandle.USER_SYSTEM); } catch (RemoteException e) { } @@ -13386,8 +13394,7 @@ public class PackageManagerService extends IPackageManager.Stub { sendPackageAddedForNewUsers(packageName, isSystem, pkgSetting.appId, userId); } - private void sendPackageAddedForNewUsers(String packageName, boolean isSystem, - int appId, int... userIds) { + public void sendPackageAddedForNewUsers(String packageName, boolean isSystem, int appId, int... userIds) { if (ArrayUtils.isEmpty(userIds)) { return; } @@ -13395,10 +13402,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userIds[0], appId)); - sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_ADDED, packageName, - extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, userIds); - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0, null, null, userIds); + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + packageName, extras, 0, null, null, userIds); if (isSystem) { mHandler.post(() -> { for (int userId : userIds) { @@ -13514,7 +13519,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting, int userId) { - final PackageRemovedInfo info = new PackageRemovedInfo(); + final PackageRemovedInfo info = new PackageRemovedInfo(this); info.removedPackage = packageName; info.removedUsers = new int[] {userId}; info.broadcastUsers = new int[] {userId}; @@ -13975,7 +13980,8 @@ public class PackageManagerService extends IPackageManager.Stub { } /** - * Get the verification agent timeout. + * Get the verification agent timeout. Used for both the APK verifier and the + * intent filter verifier. * * @return verification timeout in milliseconds */ @@ -16150,7 +16156,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Update what is removed - res.removedInfo = new PackageRemovedInfo(); + res.removedInfo = new PackageRemovedInfo(this); res.removedInfo.uid = oldPackage.applicationInfo.uid; res.removedInfo.removedPackage = oldPackage.packageName; res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null; @@ -16180,7 +16186,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (!childPackageUpdated) { - PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(); + PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); childRemovedRes.removedPackage = childPkg.packageName; childRemovedRes.isUpdate = false; childRemovedRes.dataRemoved = true; @@ -16870,7 +16876,7 @@ public class PackageManagerService extends IPackageManager.Stub { sUserManager.getUserIds(), true); } if ((mPackages.containsKey(childPkg.packageName))) { - childRes.removedInfo = new PackageRemovedInfo(); + childRes.removedInfo = new PackageRemovedInfo(this); childRes.removedInfo.removedPackage = childPkg.packageName; } if (res.addedChildPackages == null) { @@ -17593,7 +17599,8 @@ public class PackageManagerService extends IPackageManager.Stub { for (int i = 0; i < versionCount; i++) { SharedLibraryEntry libEntry = versionedLib.valueAt(i); if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey( - libEntry.info.getVersion()) < 0) { + // TODO: Remove cast for lib version once internally we support longs. + (int) libEntry.info.getVersion()) < 0) { continue; } // TODO: We will change version code to long, so in the new API it is long @@ -17716,7 +17723,7 @@ public class PackageManagerService extends IPackageManager.Stub { * sending a broadcast if necessary */ private int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) { - final PackageRemovedInfo info = new PackageRemovedInfo(); + final PackageRemovedInfo info = new PackageRemovedInfo(this); final boolean res; final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0 @@ -17816,7 +17823,8 @@ public class PackageManagerService extends IPackageManager.Stub { return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } - class PackageRemovedInfo { + static class PackageRemovedInfo { + final PackageSender packageSender; String removedPackage; int uid = -1; int removedAppId = -1; @@ -17834,6 +17842,10 @@ public class PackageManagerService extends IPackageManager.Stub { ArrayMap<String, PackageRemovedInfo> removedChildPackages; ArrayMap<String, PackageInstalledInfo> appearedChildPackages; + PackageRemovedInfo(PackageSender packageSender) { + this.packageSender = packageSender; + } + void sendPackageRemovedBroadcasts(boolean killApp) { sendPackageRemovedBroadcastInternal(killApp); final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0; @@ -17862,8 +17874,9 @@ public class PackageManagerService extends IPackageManager.Stub { ? appearedChildPackages.size() : 0; for (int i = 0; i < packageCount; i++) { PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i); - sendPackageAddedForNewUsers(installedInfo.name, true, - UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers); + packageSender.sendPackageAddedForNewUsers(installedInfo.name, + true, UserHandle.getAppId(installedInfo.uid), + installedInfo.newUsers); } } @@ -17871,12 +17884,12 @@ public class PackageManagerService extends IPackageManager.Stub { Bundle extras = new Bundle(2); extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, - extras, 0, null, null, null); - sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage, - extras, 0, null, null, null); - sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, - null, 0, removedPackage, null, null); + packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + removedPackage, extras, 0, null, null, null); + packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, + removedPackage, extras, 0, null, null, null); + packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, + null, null, 0, removedPackage, null, null); } private void sendPackageRemovedBroadcastInternal(boolean killApp) { @@ -17895,19 +17908,37 @@ public class PackageManagerService extends IPackageManager.Stub { } extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers); if (removedPackage != null) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, 0, null, null, broadcastUsers); + packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, + removedPackage, extras, 0, null, null, broadcastUsers); if (dataRemoved && !isRemovedPackageSystemUpdate) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, - removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, broadcastUsers); + packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, + removedPackage, extras, + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, + null, null, broadcastUsers); } } if (removedAppId >= 0) { - sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, + packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, broadcastUsers); } } + + void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) { + removedUsers = userIds; + if (removedUsers == null) { + broadcastUsers = null; + return; + } + + broadcastUsers = EMPTY_INT_ARRAY; + for (int i = userIds.length - 1; i >= 0; --i) { + final int userId = userIds[i]; + if (deletedPackageSetting.getInstantApp(userId)) { + continue; + } + broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId); + } + } } /* @@ -17931,23 +17962,8 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedPackage = packageName; outInfo.isStaticSharedLib = deletedPkg != null && deletedPkg.staticSharedLibName != null; - outInfo.removedUsers = deletedPs != null - ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true) - : null; - if (outInfo.removedUsers == null) { - outInfo.broadcastUsers = null; - } else { - outInfo.broadcastUsers = EMPTY_INT_ARRAY; - int[] allUsers = outInfo.removedUsers; - for (int i = allUsers.length - 1; i >= 0; --i) { - final int userId = allUsers[i]; - if (deletedPs.getInstantApp(userId)) { - continue; - } - outInfo.broadcastUsers = - ArrayUtils.appendInt(outInfo.broadcastUsers, userId); - } - } + outInfo.populateUsers(deletedPs == null ? null + : deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs); } } @@ -18430,7 +18446,7 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedChildPackages = new ArrayMap<>(childCount); for (int i = 0; i < childCount; i++) { String childPackageName = ps.childPackageNames.get(i); - PackageRemovedInfo childInfo = new PackageRemovedInfo(); + PackageRemovedInfo childInfo = new PackageRemovedInfo(this); childInfo.removedPackage = childPackageName; outInfo.removedChildPackages.put(childPackageName, childInfo); PackageSetting childPs = mSettings.getPackageLPr(childPackageName); @@ -21692,7 +21708,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName); // Delete package internally - PackageRemovedInfo outInfo = new PackageRemovedInfo(); + PackageRemovedInfo outInfo = new PackageRemovedInfo(this); synchronized (mInstallLock) { final int deleteFlags = PackageManager.DELETE_KEEP_DATA; final boolean res; @@ -21859,7 +21875,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); final ApplicationInfo info = ps.pkg.applicationInfo; final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(); + final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, "unloadPrivatePackagesInner")) { @@ -23642,4 +23658,28 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); return mInstantAppInstallerActivity == null ? null : mInstantAppInstallerActivity.getComponentName(); } + + @Override + public String getInstantAppAndroidId(String packageName, int userId) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS, + "getInstantAppAndroidId"); + enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, false /* checkShell */, + "getInstantAppAndroidId"); + // Make sure the target is an Instant App. + if (!isInstantApp(packageName, userId)) { + return null; + } + synchronized (mPackages) { + return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId); + } + } +} + +interface PackageSender { + void sendPackageBroadcast(final String action, final String pkg, + final Bundle extras, final int flags, final String targetPkg, + final IIntentReceiver finishedReceiver, final int[] userIds); + void sendPackageAddedForNewUsers(String packageName, boolean isSystem, + int appId, int... userIds); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4f29bfa2829e..5c9f7497dac9 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -222,7 +222,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; -import android.widget.ImageView; +import android.view.inputmethod.InputMethodManagerInternal; import com.android.internal.R; import com.android.internal.logging.MetricsLogger; @@ -279,6 +279,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2; static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3; static final int SHORT_PRESS_POWER_GO_HOME = 4; + static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5; static final int LONG_PRESS_POWER_NOTHING = 0; static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1; @@ -407,6 +408,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManager mPowerManager; ActivityManagerInternal mActivityManagerInternal; InputManagerInternal mInputManagerInternal; + InputMethodManagerInternal mInputMethodManagerInternal; DreamManagerInternal mDreamManagerInternal; PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; @@ -513,6 +515,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mPictureInPictureVisible; // Written by vr manager thread, only read in this class volatile boolean mPersistentVrModeEnabled; + volatile private boolean mDismissImeOnBackKeyPressed; // Used to hold the last user key used to wake the device. This helps us prevent up events // from being passed to the foregrounded app without a corresponding down event @@ -1396,6 +1399,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { case SHORT_PRESS_POWER_GO_HOME: launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/); break; + case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: { + if (mDismissImeOnBackKeyPressed) { + if (mInputMethodManagerInternal == null) { + mInputMethodManagerInternal = + LocalServices.getService(InputMethodManagerInternal.class); + } + if (mInputMethodManagerInternal != null) { + mInputMethodManagerInternal.hideCurrentInputMethod(); + } + } else { + launchHomeFromHotKey(true /* awakenFromDreams */, + false /*respectKeyguard*/); + } + break; + } } } } @@ -7955,6 +7973,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void setDismissImeOnBackKeyPressed(boolean newValue) { + mDismissImeOnBackKeyPressed = newValue; + } + + @Override public int getInputMethodWindowVisibleHeightLw() { return mDockBottom - mCurBottom; } @@ -8170,6 +8193,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mLastInputMethodTargetWindow="); pw.println(mLastInputMethodTargetWindow); } + pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed="); + pw.println(mDismissImeOnBackKeyPressed); if (mStatusBar != null) { pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); pw.print(" isStatusBarKeyguard="); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 8f114361ccff..f5bb082fac6d 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -406,11 +406,7 @@ final class Notifier { mHandler.post(new Runnable() { @Override public void run() { - LogMaker log = new LogMaker(MetricsEvent.SCREEN); - log.setType(MetricsEvent.TYPE_OPEN); - log.setSubtype(0); // not user initiated - MetricsLogger.action(log); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); + // Note a SCREEN tron event is logged in PowerManagerService. mPolicy.startedWakingUp(); } }); @@ -470,7 +466,7 @@ final class Notifier { log.setType(MetricsEvent.TYPE_CLOSE); log.setSubtype(why); MetricsLogger.action(log); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0); + EventLogTags.writePowerScreenState(0, why, 0, 0, 0); mPolicy.finishedGoingToSleep(why); } }); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index cf597b052c10..4f239a5cba23 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -32,6 +32,7 @@ import android.hardware.SystemSensorManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.V1_0.PowerHint; +import android.metrics.LogMaker; import android.net.Uri; import android.os.BatteryManager; import android.os.BatteryManagerInternal; @@ -75,6 +76,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -188,6 +191,11 @@ public final class PowerManagerService extends SystemService // System property indicating that the screen should remain off until an explicit user action private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; + private static final String TRACE_SCREEN_ON = "Screen turning on"; + + /** If turning screen on takes more than this long, we show a warning on logcat. */ + private static final int SCREEN_ON_LATENCY_WARNING_MS = 200; + /** Constants for {@link #shutdownOrRebootInternal} */ @Retention(RetentionPolicy.SOURCE) @IntDef({HALT_MODE_SHUTDOWN, HALT_MODE_REBOOT, HALT_MODE_REBOOT_SAFE_MODE}) @@ -1369,6 +1377,8 @@ public final class PowerManagerService extends SystemService return false; } + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); try { switch (mWakefulness) { @@ -1551,6 +1561,23 @@ public final class PowerManagerService extends SystemService } } + private void logScreenOn() { + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); + + final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime); + + LogMaker log = new LogMaker(MetricsEvent.SCREEN); + log.setType(MetricsEvent.TYPE_OPEN); + log.setSubtype(0); // not user initiated + log.setLatency(latencyMs); // How long it took. + MetricsLogger.action(log); + EventLogTags.writePowerScreenState(1, 0, 0, 0, latencyMs); + + if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { + Slog.w(TAG, "Screen on took " + latencyMs+ " ms"); + } + } + private void finishWakefulnessChangeIfNeededLocked() { if (mWakefulnessChanging && mDisplayReady) { if (mWakefulness == WAKEFULNESS_DOZING @@ -1560,6 +1587,9 @@ public final class PowerManagerService extends SystemService if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) { logSleepTimeoutRecapturedLocked(); } + if (mWakefulness == WAKEFULNESS_AWAKE) { + logScreenOn(); + } mWakefulnessChanging = false; mNotifier.onWakefulnessChangeFinished(); } diff --git a/services/core/java/com/android/server/storage/AppCollector.java b/services/core/java/com/android/server/storage/AppCollector.java index a77d33f8514b..03b754f777b3 100644 --- a/services/core/java/com/android/server/storage/AppCollector.java +++ b/services/core/java/com/android/server/storage/AppCollector.java @@ -21,22 +21,21 @@ import android.app.usage.StorageStats; import android.app.usage.StorageStatsManager; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageStats; import android.content.pm.UserInfo; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.Process; -import android.os.RemoteException; import android.os.UserManager; import android.os.storage.VolumeInfo; import android.util.Log; + import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -44,7 +43,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; /** * AppCollector asynchronously collects package sizes. @@ -132,7 +130,7 @@ public class AppCollector { try { StorageStats storageStats = - mStorageStatsManager.queryStatsForPackage(app.volumeUuid, + mStorageStatsManager.queryStatsForPackage(app.storageUuid, app.packageName, user.getUserHandle()); PackageStats packageStats = new PackageStats(app.packageName, user.id); @@ -140,7 +138,7 @@ public class AppCollector { packageStats.codeSize = storageStats.getCodeBytes(); packageStats.dataSize = storageStats.getDataBytes(); stats.add(packageStats); - } catch (IllegalStateException e) { + } catch (NameNotFoundException | IOException e) { Log.e(TAG, "An exception occurred while fetching app size", e); } } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 1fb34eb66d20..763464422e56 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1280,6 +1280,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked(). dc.setLayoutNeeded(); mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); + + final TaskStack s = getStack(); + if (s != null) { + s.onAllWindowsDrawn(); + } } } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index f41eed53cbca..7f3c89c58a28 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -22,6 +22,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.animation.Animator; import android.animation.ValueAnimator; +import android.annotation.IntDef; import android.content.Context; import android.graphics.Rect; import android.os.Handler; @@ -35,6 +36,9 @@ import android.view.WindowManagerInternal; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Enables animating bounds of objects. * @@ -43,7 +47,7 @@ import com.android.internal.annotations.VisibleForTesting; * relaunching it would cause poorer experience), these class provides a way to directly animate * the bounds of the resized object. * - * The object that is resized needs to implement {@link AnimateBoundsUser} interface. + * The object that is resized needs to implement {@link BoundsAnimationTarget} interface. * * NOTE: All calls to methods in this class should be done on the UI thread */ @@ -56,8 +60,19 @@ public class BoundsAnimationController { private static final int DEFAULT_TRANSITION_DURATION = 425; + @Retention(RetentionPolicy.SOURCE) + @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START, + SCHEDULE_PIP_MODE_CHANGED_ON_END}) + public @interface SchedulePipModeChangedState {} + /** Do not schedule any PiP mode changed callbacks as a part of this animation. */ + public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0; + /** Schedule a PiP mode changed callback when this animation starts. */ + public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1; + /** Schedule a PiP mode changed callback when this animation ends. */ + public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; + // Only accessed on UI thread. - private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); + private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); private final class AppTransitionNotifier extends WindowManagerInternal.AppTransitionListener implements Runnable { @@ -97,6 +112,8 @@ public class BoundsAnimationController { private final Interpolator mFastOutSlowInInterpolator; private boolean mFinishAnimationAfterTransition = false; + private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; + BoundsAnimationController(Context context, AppTransition transition, Handler handler) { mHandler = handler; mAppTransition = transition; @@ -108,40 +125,42 @@ public class BoundsAnimationController { @VisibleForTesting final class BoundsAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { - private final AnimateBoundsUser mTarget; + private final BoundsAnimationTarget mTarget; private final Rect mFrom = new Rect(); private final Rect mTo = new Rect(); private final Rect mTmpRect = new Rect(); private final Rect mTmpTaskBounds = new Rect(); - private final boolean mMoveToFullScreen; - // True if this this animation was cancelled and will be replaced the another animation from - // the same {@link #AnimateBoundsUser} target. + + // True if this this animation was canceled and will be replaced the another animation from + // the same {@link #BoundsAnimationTarget} target. private boolean mSkipFinalResize; // True if this animation replaced a previous animation of the same - // {@link #AnimateBoundsUser} target. + // {@link #BoundsAnimationTarget} target. private final boolean mSkipAnimationStart; - // True if this animation was cancelled by the user, not as a part of a replacing animation + // True if this animation was canceled by the user, not as a part of a replacing animation private boolean mSkipAnimationEnd; - // True if this animation is not replacing a previous animation, or if the previous - // animation is animating to a different fullscreen state than the current animation. - // We use this to ensure that we always provide a consistent set/order of callbacks when we - // transition to/from PiP. - private final boolean mAnimatingToNewFullscreenState; + // True if the animation target should be moved to the fullscreen stack at the end of this + // animation + private boolean mMoveToFullscreen; + + // Whether to schedule PiP mode changes on animation start/end + private @SchedulePipModeChangedState int mSchedulePipModeChangedState; // Depending on whether we are animating from // a smaller to a larger size private final int mFrozenTaskWidth; private final int mFrozenTaskHeight; - BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen, - boolean replacingExistingAnimation, boolean animatingToNewFullscreenState) { + BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to, + @SchedulePipModeChangedState int schedulePipModeChangedState, + boolean moveToFullscreen, boolean replacingExistingAnimation) { super(); mTarget = target; mFrom.set(from); mTo.set(to); - mMoveToFullScreen = moveToFullScreen; mSkipAnimationStart = replacingExistingAnimation; - mAnimatingToNewFullscreenState = animatingToNewFullscreenState; + mSchedulePipModeChangedState = schedulePipModeChangedState; + mMoveToFullscreen = moveToFullscreen; addUpdateListener(this); addListener(this); @@ -158,10 +177,18 @@ public class BoundsAnimationController { } } + final Runnable mResumeRunnable = new Runnable() { + @Override + public void run() { + resume(); + } + }; + @Override public void onAnimationStart(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget - + " mSkipAnimationStart=" + mSkipAnimationStart); + + " mSkipAnimationStart=" + mSkipAnimationStart + + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState); mFinishAnimationAfterTransition = false; mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth, mFrom.top + mFrozenTaskHeight); @@ -170,23 +197,34 @@ public class BoundsAnimationController { // we trigger any size changes, so it can swap surfaces // in to appropriate modes, or do as it wishes otherwise. if (!mSkipAnimationStart) { - mTarget.onAnimationStart(mMoveToFullScreen); - } - - // If we are animating to a new fullscreen state (either to/from fullscreen), then - // notify the target of the change with the new frozen task bounds - if (mAnimatingToNewFullscreenState && mMoveToFullScreen) { - mTarget.updatePictureInPictureMode(null); + mTarget.onAnimationStart(mSchedulePipModeChangedState == + SCHEDULE_PIP_MODE_CHANGED_ON_START); } // Immediately update the task bounds if they have to become larger, but preserve // the starting position so we don't jump at the beginning of the animation. if (animatingToLargerSize()) { mTarget.setPinnedStackSize(mFrom, mTmpRect); + + // We pause the animation until the app has drawn at the new size. + // The target will notify us via BoundsAnimationController#resume. + // We do this here and pause the animation, rather than just defer starting it + // so we can enter the animating state and have WindowStateAnimator apply the + // correct logic to make this resize seamless. + if (mMoveToFullscreen) { + pause(); + mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS); + } } } @Override + public void resume() { + mHandler.removeCallbacks(mResumeRunnable); + super.resume(); + } + + @Override public void onAnimationUpdate(ValueAnimator animation) { final float value = (Float) animation.getAnimatedValue(); final float remains = 1 - value; @@ -206,20 +244,26 @@ public class BoundsAnimationController { // any further animation. if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled"); + // If we have already scheduled a PiP mode changed at the start of the animation, + // then we need to clean up and schedule one at the end, since we have canceled the + // animation to the final state. + if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { + mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; + } + // Since we are cancelling immediately without a replacement animation, send the // animation end to maintain callback parity, but also skip any further resizes - prepareCancel(false /* skipAnimationEnd */, true /* skipFinalResize */); - cancel(); + cancelAndCallAnimationEnd(); } } @Override public void onAnimationEnd(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " mMoveToFullScreen=" + mMoveToFullScreen + " mSkipFinalResize=" + mSkipFinalResize + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition - + " mAppTransitionIsRunning=" + mAppTransition.isRunning()); + + " mAppTransitionIsRunning=" + mAppTransition.isRunning() + + " callers=" + Debug.getCallers(2)); // There could be another animation running. For example in the // move to fullscreen case, recents will also be closing while the @@ -231,58 +275,57 @@ public class BoundsAnimationController { return; } - if (!mSkipFinalResize) { - // If not cancelled, resize the pinned stack to the final size. All calls to - // setPinnedStackSize() must be done between onAnimationStart() and onAnimationEnd() - mTarget.setPinnedStackSize(mTo, null); + if (!mSkipAnimationEnd) { + // If this animation has already scheduled the picture-in-picture mode on start, and + // we are not skipping the final resize due to being canceled, then move the PiP to + // fullscreen once the animation ends + if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget + + " moveToFullscreen=" + mMoveToFullscreen); + mTarget.onAnimationEnd(mSchedulePipModeChangedState == + SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null, + mMoveToFullscreen); } - finishAnimation(); - - if (mMoveToFullScreen && !mSkipFinalResize) { - mTarget.moveToFullscreen(); - } + // Clean up this animation + removeListener(this); + removeUpdateListener(this); + mRunningAnimations.remove(mTarget); } @Override public void onAnimationCancel(Animator animation) { - finishAnimation(); + // Always skip the final resize when the animation is canceled + mSkipFinalResize = true; + mMoveToFullscreen = false; } - public void prepareCancel(boolean skipAnimationEnd, boolean skipFinalResize) { - if (DEBUG) Slog.d(TAG, "prepareCancel: skipAnimationEnd=" + skipAnimationEnd - + " skipFinalResize=" + skipFinalResize); - mSkipAnimationEnd = skipAnimationEnd; - mSkipFinalResize = skipFinalResize; + private void cancelAndCallAnimationEnd() { + if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget); + mSkipAnimationEnd = false; + super.cancel(); } @Override public void cancel() { if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget); + mSkipAnimationEnd = true; super.cancel(); } - /** Returns true if the animation target is the same as the input bounds. */ + /** + * @return true if the animation target is the same as the input bounds. + */ boolean isAnimatingTo(Rect bounds) { return mTo.equals(bounds); } - private boolean animatingToLargerSize() { - if (mFrom.width() * mFrom.height() > mTo.width() * mTo.height()) { - return false; - } - return true; - } - - private void finishAnimation() { - if (DEBUG) Slog.d(TAG, "finishAnimation: mTarget=" + mTarget - + " callers" + Debug.getCallers(2)); - if (!mSkipAnimationEnd) { - mTarget.onAnimationEnd(); - } - removeListener(this); - removeUpdateListener(this); - mRunningAnimations.remove(mTarget); + /** + * @return true if we are animating to a larger surface size + */ + @VisibleForTesting + boolean animatingToLargerSize() { + // TODO: Fix this check for aspect ratio changes + return (mFrom.width() * mFrom.height() <= mTo.width() * mTo.height()); } @Override @@ -291,63 +334,23 @@ public class BoundsAnimationController { } } - public interface AnimateBoundsUser { - /** - * Sets the size of the target (without any intermediate steps, like scheduling animation) - * but freezes the bounds of any tasks in the target at taskBounds, - * to allow for more flexibility during resizing. Only works for the pinned stack at the - * moment. - * - * @return Whether the target should continue to be animated and this call was successful. - * If false, the animation will be cancelled because the user has determined that the - * animation is now invalid and not required. In such a case, the cancel will trigger the - * animation end callback as well, but will not send any further size changes. - */ - boolean setPinnedStackSize(Rect bounds, Rect taskBounds); - - /** - * Callback for the target to inform it that the animation has started, so it can do some - * necessary preparation. - */ - void onAnimationStart(boolean toFullscreen); - - /** - * Callback for the target to inform it that the animation is going to a new fullscreen - * state and should update the picture-in-picture mode accordingly. - * - * @param targetStackBounds the target stack bounds we are animating to, can be null if - * we are animating to fullscreen - */ - void updatePictureInPictureMode(Rect targetStackBounds); - - /** - * Callback for the target to inform it that the animation has ended, so it can do some - * necessary cleanup. - */ - void onAnimationEnd(); - - /** - * Callback for the target to inform it to reparent to the fullscreen stack. - */ - void moveToFullscreen(); - } - - public void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, - int animationDuration, boolean moveToFullscreen) { - animateBoundsImpl(target, from, to, animationDuration, moveToFullscreen); + public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, + int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, + boolean moveToFullscreen) { + animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, + moveToFullscreen); } @VisibleForTesting - BoundsAnimator animateBoundsImpl(final AnimateBoundsUser target, Rect from, Rect to, - int animationDuration, boolean moveToFullscreen) { + BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, + int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, + boolean moveToFullscreen) { final BoundsAnimator existing = mRunningAnimations.get(target); final boolean replacing = existing != null; - final boolean animatingToNewFullscreenState = (existing == null) || - (existing.mMoveToFullScreen != moveToFullscreen); if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to - + " moveToFullscreen=" + moveToFullscreen + " replacing=" + replacing - + " animatingToNewFullscreenState=" + animatingToNewFullscreenState); + + " schedulePipModeChangedState=" + schedulePipModeChangedState + + " replacing=" + replacing); if (replacing) { if (existing.isAnimatingTo(to)) { @@ -355,15 +358,36 @@ public class BoundsAnimationController { // one we are trying to start. if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing + " ignoring..."); + return existing; } - // Since we are replacing, we skip both animation start and end callbacks, and don't - // animate to the final bounds when cancelling - existing.prepareCancel(true /* skipAnimationEnd */, true /* skipFinalResize */); + + // Update the PiP callback states if we are replacing the animation + if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { + if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { + if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep" + + " existing deferred state"); + } else { + if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback" + + " on start already processed, schedule deferred update on end"); + schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; + } + } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) { + if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { + if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled," + + " callback on start will be processed"); + } else { + if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep" + + " existing deferred state"); + schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; + } + } + + // Since we are replacing, we skip both animation start and end callbacks existing.cancel(); } - final BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen, - replacing, animatingToNewFullscreenState); + final BoundsAnimator animator = new BoundsAnimator(target, from, to, + schedulePipModeChangedState, moveToFullscreen, replacing); mRunningAnimations.put(target, animator); animator.setFloatValues(0f, 1f); animator.setDuration((animationDuration != -1 ? animationDuration @@ -372,4 +396,15 @@ public class BoundsAnimationController { animator.start(); return animator; } + + private void resume() { + for (int i = 0; i < mRunningAnimations.size(); i++) { + final BoundsAnimator b = mRunningAnimations.valueAt(i); + b.resume(); + } + } + + public void onAllWindowsDrawn() { + mHandler.post(this::resume); + } } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java new file mode 100644 index 000000000000..8b1bf7bf77dc --- /dev/null +++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.graphics.Rect; + +/** + * The target for a BoundsAnimation. + * @see BoundsAnimationController + */ +interface BoundsAnimationTarget { + + /** + * Callback for the target to inform it that the animation has started, so it can do some + * necessary preparation. + * + * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed + * callbacks + */ + void onAnimationStart(boolean schedulePipModeChangedCallback); + + /** + * Sets the size of the target (without any intermediate steps, like scheduling animation) + * but freezes the bounds of any tasks in the target at taskBounds, to allow for more + * flexibility during resizing. Only works for the pinned stack at the moment. This will + * only be called between onAnimationStart() and onAnimationEnd(). + * + * @return Whether the target should continue to be animated and this call was successful. + * If false, the animation will be cancelled because the user has determined that the + * animation is now invalid and not required. In such a case, the cancel will trigger the + * animation end callback as well, but will not send any further size changes. + */ + boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds); + + /** + * Callback for the target to inform it that the animation has ended, so it can do some + * necessary cleanup. + * + * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed + * callbacks + * @param finalStackSize the final stack bounds to set on the target (can be to indicate that + * the animation was cancelled and the target does not need to update to the final stack bounds) + * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack + * when the animation completes + */ + void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, + boolean moveToFullscreen); +} diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 16848780fe47..82416ec513c4 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -26,7 +26,6 @@ import android.app.RemoteAction; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; @@ -123,6 +122,13 @@ class PinnedStackController { mSnapAlgorithm.setMinimized(isMinimized); }); } + + @Override + public int getDisplayRotation() { + synchronized (mService.mWindowMap) { + return mDisplayInfo.rotation; + } + } } /** @@ -221,22 +227,26 @@ class PinnedStackController { * @return the size of the PIP based on the given {@param aspectRatio}. */ Size getSize(float aspectRatio) { - return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize, - mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + synchronized (mService.mWindowMap) { + return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize, + mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + } } /** * @return the default bounds to show the PIP when there is no active PIP. */ Rect getDefaultBounds() { - final Rect insetBounds = new Rect(); - getInsetBounds(insetBounds); - - final Rect defaultBounds = new Rect(); - final Size size = getSize(mDefaultAspectRatio); - Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds, - 0, mIsImeShowing ? mImeHeight : 0, defaultBounds); - return defaultBounds; + synchronized (mService.mWindowMap) { + final Rect insetBounds = new Rect(); + getInsetBounds(insetBounds); + + final Rect defaultBounds = new Rect(); + final Size size = getSize(mDefaultAspectRatio); + Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds, + 0, mIsImeShowing ? mImeHeight : 0, defaultBounds); + return defaultBounds; + } } /** @@ -254,42 +264,44 @@ class PinnedStackController { * new orientation of the device if necessary. */ boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) { - final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); - if (mDisplayInfo.equals(displayInfo)) { - // We are already in the right orientation, ignore - outBounds.setEmpty(); - return false; - } else if (targetBounds.isEmpty()) { - // The stack is null, we are just initializing the stack, so just store the display info - // and ignore + synchronized (mService.mWindowMap) { + final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); + if (mDisplayInfo.equals(displayInfo)) { + // We are already in the right orientation, ignore + outBounds.setEmpty(); + return false; + } else if (targetBounds.isEmpty()) { + // The stack is null, we are just initializing the stack, so just store the display + // info and ignore + mDisplayInfo.copyFrom(displayInfo); + outBounds.setEmpty(); + return false; + } + + mTmpRect.set(targetBounds); + final Rect postChangeStackBounds = mTmpRect; + + // Calculate the snap fraction of the current stack along the old movement bounds + final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds); + final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds, + preChangeMovementBounds); mDisplayInfo.copyFrom(displayInfo); - outBounds.setEmpty(); - return false; - } - mTmpRect.set(targetBounds); - final Rect postChangeStackBounds = mTmpRect; - - // Calculate the snap fraction of the current stack along the old movement bounds - final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds); - final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds, - preChangeMovementBounds); - mDisplayInfo.copyFrom(displayInfo); - - // Calculate the stack bounds in the new orientation to the same same fraction along the - // rotated movement bounds. - final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds, - false /* adjustForIme */); - mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, - snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds); - } + // Calculate the stack bounds in the new orientation to the same same fraction along the + // rotated movement bounds. + final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds, + false /* adjustForIme */); + mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, + snapFraction); + if (mIsMinimized) { + applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds); + } - notifyMovementBoundsChanged(false /* fromImeAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); - outBounds.set(postChangeStackBounds); - return true; + outBounds.set(postChangeStackBounds); + return true; + } } /** @@ -378,25 +390,27 @@ class PinnedStackController { * Notifies listeners that the PIP movement bounds have changed. */ private void notifyMovementBoundsChanged(boolean fromImeAdjustement) { - if (mPinnedStackListener != null) { - try { - final Rect insetBounds = new Rect(); - getInsetBounds(insetBounds); - final Rect normalBounds = getDefaultBounds(); - if (isValidPictureInPictureAspectRatio(mAspectRatio)) { - transformBoundsToAspectRatio(normalBounds, mAspectRatio); - } - final Rect animatingBounds = mTmpAnimatingBoundsRect; - final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID); - if (pinnedStack != null) { - pinnedStack.getAnimationOrCurrentBounds(animatingBounds); - } else { - animatingBounds.set(normalBounds); + synchronized (mService.mWindowMap) { + if (mPinnedStackListener != null) { + try { + final Rect insetBounds = new Rect(); + getInsetBounds(insetBounds); + final Rect normalBounds = getDefaultBounds(); + if (isValidPictureInPictureAspectRatio(mAspectRatio)) { + transformBoundsToAspectRatio(normalBounds, mAspectRatio); + } + final Rect animatingBounds = mTmpAnimatingBoundsRect; + final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID); + if (pinnedStack != null) { + pinnedStack.getAnimationOrCurrentBounds(animatingBounds); + } else { + animatingBounds.set(normalBounds); + } + mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds, + animatingBounds, fromImeAdjustement, mDisplayInfo.rotation); + } catch (RemoteException e) { + Slog.e(TAG_WM, "Error delivering actions changed event.", e); } - mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds, - animatingBounds, fromImeAdjustement); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering actions changed event.", e); } } } @@ -405,11 +419,13 @@ class PinnedStackController { * @return the bounds on the screen that the PIP can be visible in. */ private void getInsetBounds(Rect outRect) { - mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth, - mDisplayInfo.logicalHeight, mTmpInsets); - outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y, - mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x, - mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y); + synchronized (mService.mWindowMap) { + mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth, + mDisplayInfo.logicalHeight, mTmpInsets); + outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y, + mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x, + mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y); + } } /** @@ -417,7 +433,9 @@ class PinnedStackController { * controller. */ private Rect getMovementBounds(Rect stackBounds) { - return getMovementBounds(stackBounds, true /* adjustForIme */); + synchronized (mService.mWindowMap) { + return getMovementBounds(stackBounds, true /* adjustForIme */); + } } /** @@ -425,23 +443,27 @@ class PinnedStackController { * controller. */ private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) { - final Rect movementBounds = new Rect(); - getInsetBounds(movementBounds); - - // Apply the movement bounds adjustments based on the current state - mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds, - (adjustForIme && mIsImeShowing) ? mImeHeight : 0); - return movementBounds; + synchronized (mService.mWindowMap) { + final Rect movementBounds = new Rect(); + getInsetBounds(movementBounds); + + // Apply the movement bounds adjustments based on the current state + mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds, + (adjustForIme && mIsImeShowing) ? mImeHeight : 0); + return movementBounds; + } } /** * Applies the minimized offsets to the given stack bounds. */ private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) { - mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); - mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets); - mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize, - mStableInsets); + synchronized (mService.mWindowMap) { + mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets); + mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize, + mStableInsets); + } } /** diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java index 135832e619a4..5b9c16ce60c9 100644 --- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java +++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java @@ -17,6 +17,10 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; +import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; import android.app.RemoteAction; import android.graphics.Rect; @@ -40,36 +44,53 @@ public class PinnedStackWindowController extends StackWindowController { /** * Animates the pinned stack. */ - public void animateResizePinnedStack(Rect sourceBounds, Rect destBounds, - int animationDuration) { + public void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, + int animationDuration, boolean schedulePipModeChangedOnAnimationEnd) { synchronized (mWindowMap) { if (mContainer == null) { throw new IllegalArgumentException("Pinned stack container not found :("); } - // Get non-null fullscreen bounds if the bounds are null - final boolean moveToFullscreen = destBounds == null; - destBounds = getPinnedStackAnimationBounds(destBounds); - - // If the bounds are truly null, then there was no fullscreen stack at this time, so - // animate this to the full display bounds - final Rect toBounds; - if (destBounds == null) { - toBounds = new Rect(); - mContainer.getDisplayContent().getLogicalDisplayRect(toBounds); - } else { - toBounds = destBounds; + // Get the from-bounds + final Rect fromBounds = new Rect(); + mContainer.getBounds(fromBounds); + + // Get non-null fullscreen to-bounds for animating if the bounds are null + @SchedulePipModeChangedState int schedulePipModeChangedState = + NO_PIP_MODE_CHANGED_CALLBACKS; + final boolean toFullscreen = toBounds == null; + if (toFullscreen) { + if (schedulePipModeChangedOnAnimationEnd) { + throw new IllegalArgumentException("Should not defer scheduling PiP mode" + + " change on animation to fullscreen."); + } + schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; + + mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect); + if (!mTmpBoundsRect.isEmpty()) { + // If there is a fullscreen bounds, use that + toBounds = new Rect(mTmpBoundsRect); + } else { + // Otherwise, use the display bounds + toBounds = new Rect(); + mContainer.getDisplayContent().getLogicalDisplayRect(toBounds); + } + } else if (schedulePipModeChangedOnAnimationEnd) { + schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; } - final Rect originalBounds = new Rect(); - mContainer.getBounds(originalBounds); - mContainer.setAnimationFinalBounds(sourceBounds, toBounds); + mContainer.setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); + + final Rect finalToBounds = toBounds; + final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = + schedulePipModeChangedState; UiThread.getHandler().post(() -> { if (mContainer == null) { return; } - mService.mBoundsAnimationController.animateBounds(mContainer, originalBounds, - toBounds, animationDuration, moveToFullscreen); + mService.mBoundsAnimationController.animateBounds(mContainer, fromBounds, + finalToBounds, animationDuration, finalSchedulePipModeChangedState, + toFullscreen); }); } } @@ -93,7 +114,8 @@ public class PinnedStackWindowController extends StackWindowController { if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) { if (!toBounds.equals(targetBounds)) { - animateResizePinnedStack(null /* sourceBounds */, toBounds, -1 /* duration */); + animateResizePinnedStack(toBounds, null /* sourceHintBounds */, + -1 /* duration */, false /* schedulePipModeChangedOnAnimationEnd */); } pinnedStackController.setAspectRatio( pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) @@ -116,26 +138,31 @@ public class PinnedStackWindowController extends StackWindowController { } /** - * @return whether the bounds are currently animating to fullscreen. + * @return whether the multi-window mode change should be deferred as a part of a transition + * from fullscreen to non-fullscreen bounds. */ - public boolean isAnimatingBoundsToFullscreen() { - return mContainer.isAnimatingBoundsToFullscreen(); + public boolean deferScheduleMultiWindowModeChanged() { + synchronized(mWindowMap) { + return mContainer.deferScheduleMultiWindowModeChanged(); + } } - public boolean pinnedStackResizeAllowed() { - return mContainer.pinnedStackResizeAllowed(); + /** + * @return whether the bounds are currently animating to fullscreen. + */ + public boolean isAnimatingBoundsToFullscreen() { + synchronized (mWindowMap) { + return mContainer.isAnimatingBoundsToFullscreen(); + } } /** - * Checks the {@param bounds} and retirms non-null fullscreen bounds for the pinned stack - * animation if necessary. + * @return whether the stack can be resized from the bounds animation. */ - private Rect getPinnedStackAnimationBounds(Rect bounds) { - mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect); - if (bounds == null && !mTmpBoundsRect.isEmpty()) { - bounds = new Rect(mTmpBoundsRect); + public boolean pinnedStackResizeDisallowed() { + synchronized (mWindowMap) { + return mContainer.pinnedStackResizeDisallowed(); } - return bounds; } /** diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index d7c41d335b72..1feb743b66dc 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -52,11 +52,12 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; import com.android.server.EventLogTags; +import com.android.server.UiThread; import java.io.PrintWriter; public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser, - BoundsAnimationController.AnimateBoundsUser { + BoundsAnimationTarget { /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to * restrict IME adjustment so that a min portion of top stack remains visible.*/ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; @@ -132,7 +133,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye private boolean mBoundsAnimatingToFullscreen = false; private boolean mCancelCurrentBoundsAnimation = false; private Rect mBoundsAnimationTarget = new Rect(); - private Rect mBoundsAnimationSourceBounds = new Rect(); + private Rect mBoundsAnimationSourceHintBounds = new Rect(); // 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(). @@ -322,18 +323,19 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye * Sets the bounds animation target bounds ahead of an animation. This can't currently be done * in onAnimationStart() since that is started on the UiThread. */ - void setAnimationFinalBounds(Rect sourceBounds, Rect destBounds) { + void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) { mBoundsAnimatingRequested = true; - if (sourceBounds != null) { - mBoundsAnimationSourceBounds.set(sourceBounds); - } else { - mBoundsAnimationSourceBounds.setEmpty(); - } + mBoundsAnimatingToFullscreen = toFullscreen; if (destBounds != null) { mBoundsAnimationTarget.set(destBounds); } else { mBoundsAnimationTarget.setEmpty(); } + if (sourceHintBounds != null) { + mBoundsAnimationSourceHintBounds.set(sourceHintBounds); + } else { + mBoundsAnimationSourceHintBounds.setEmpty(); + } } /** @@ -346,8 +348,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye /** * @return the final source bounds for the bounds animation. */ - void getFinalAnimationSourceBounds(Rect outBounds) { - outBounds.set(mBoundsAnimationSourceBounds); + void getFinalAnimationSourceHintBounds(Rect outBounds) { + outBounds.set(mBoundsAnimationSourceHintBounds); } /** @@ -413,7 +415,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye // orientation, clear the animation target bounds since they are obsolete, and // cancel any currently running animations mBoundsAnimationTarget.setEmpty(); - mBoundsAnimationSourceBounds.setEmpty(); + mBoundsAnimationSourceHintBounds.setEmpty(); mCancelCurrentBoundsAnimation = true; return true; } @@ -1458,26 +1460,44 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye } } - public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) { - if (mCancelCurrentBoundsAnimation) { - return false; + public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) { + // Hold the lock since this is called from the BoundsAnimator running on the UiThread + synchronized (mService.mWindowMap) { + if (mCancelCurrentBoundsAnimation) { + return false; + } } try { - mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds); + mService.mActivityManager.resizePinnedStack(stackBounds, tempTaskBounds); } catch (RemoteException e) { // I don't believe you. } return true; } + void onAllWindowsDrawn() { + if (!mBoundsAnimating) { + return; + } + + mService.mBoundsAnimationController.onAllWindowsDrawn(); + } + @Override // AnimatesBounds - public void onAnimationStart(boolean toFullscreen) { + public void onAnimationStart(boolean schedulePipModeChangedCallback) { + // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mService.mWindowMap) { mBoundsAnimatingRequested = false; mBoundsAnimating = true; - mBoundsAnimatingToFullscreen = toFullscreen; mCancelCurrentBoundsAnimation = false; + + // If we are changing UI mode, as in the PiP to fullscreen + // transition, then we need to wait for the window to draw. + if (schedulePipModeChangedCallback) { + forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); }, + false /* traverseTopToBottom */); + } } if (mStackId == PINNED_STACK_ID) { @@ -1486,41 +1506,63 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye } catch (RemoteException e) { // I don't believe you... } - } - } - @Override // AnimatesBounds - public void updatePictureInPictureMode(Rect targetStackBounds) { - final PinnedStackWindowController controller = - (PinnedStackWindowController) getController(); - if (controller != null) { - controller.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds); + final PinnedStackWindowController controller = + (PinnedStackWindowController) getController(); + if (schedulePipModeChangedCallback && controller != null) { + // We need to schedule the PiP mode change after the animation down, so use the + // final bounds + controller.updatePictureInPictureModeForPinnedStackAnimation(null); + } } } @Override // AnimatesBounds - public void onAnimationEnd() { + public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, + boolean moveToFullscreen) { + // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mService.mWindowMap) { mBoundsAnimating = false; mService.requestTraversal(); } if (mStackId == PINNED_STACK_ID) { + final PinnedStackWindowController controller = + (PinnedStackWindowController) getController(); + if (schedulePipModeChangedCallback && controller != null) { + // We need to schedule the PiP mode change after the animation down, so use the + // final bounds + controller.updatePictureInPictureModeForPinnedStackAnimation( + mBoundsAnimationTarget); + } + + // Update to the final bounds if requested. This is done here instead of in the bounds + // animator to allow us to coordinate this after we notify the PiP mode changed + if (finalStackSize != null) { + setPinnedStackSize(finalStackSize, null); + } + try { mService.mActivityManager.notifyPinnedStackAnimationEnded(); + if (moveToFullscreen) { + mService.mActivityManager.moveTasksToFullscreenStack(mStackId, + true /* onTop */); + } } catch (RemoteException e) { // I don't believe you... } } } - @Override - public void moveToFullscreen() { - try { - mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true); - } catch (RemoteException e) { - e.printStackTrace(); + /** + * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen + * bounds and we have a deferred PiP mode changed callback set with the animation. + */ + public boolean deferScheduleMultiWindowModeChanged() { + if (mStackId == PINNED_STACK_ID) { + return (mBoundsAnimatingRequested || mBoundsAnimating); } + return false; } public boolean hasMovementAnimations() { @@ -1539,7 +1581,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye return mBoundsAnimating && mBoundsAnimatingToFullscreen; } - public boolean pinnedStackResizeAllowed() { + public boolean pinnedStackResizeDisallowed() { if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { return true; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0049585f5c52..252b4d4e0473 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7267,13 +7267,16 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void updateInputMethodWindowStatus(@NonNull IBinder imeToken, - boolean imeWindowVisible, @Nullable IBinder targetWindowToken) { + boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed, + @Nullable IBinder targetWindowToken) { // TODO (b/34628091): Use this method to address the window animation issue. if (DEBUG_INPUT_METHOD) { Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken + + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed + " imeWindowVisible=" + imeWindowVisible + " targetWindowToken=" + targetWindowToken); } + mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed); } @Override diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0b96f3fbc1fc..b9776a362b8b 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4329,7 +4329,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges, int oldVisibility) { - result |= !isVisibleLw() ? RELAYOUT_RES_FIRST_TIME : 0; + final boolean wasVisible = isVisibleLw(); + + result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0; if (mAnimatingExit) { Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit=" + mRemoveOnExit + ", mDestroying=" + mDestroying); @@ -4348,7 +4350,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mLastVisibleLayoutRotation = getDisplayContent().getRotation(); mWinAnimator.mEnteringAnimation = true; - if ((result & RELAYOUT_RES_FIRST_TIME) != 0) { + if (!wasVisible) { prepareWindowToDisplayDuringRelayout(mergedConfiguration); } if ((attrChanges & FORMAT_CHANGED) != 0) { @@ -4365,9 +4367,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // When we change the Surface size, in scenarios which may require changing // the surface position in sync with the resize, we use a preserved surface // so we can freeze it while waiting for the client to report draw on the newly - // sized surface. + // sized surface. Don't preserve surfaces if the insets change while animating the pinned + // stack since it can lead to issues if a new surface is created while calculating the + // scale for the animation using the source hint rect + // (see WindowStateAnimator#setSurfaceBoundariesLocked()). if (isDragResizeChanged() || isResizedWhileNotDragResizing() - || surfaceInsetsChanging()) { + || (surfaceInsetsChanging() && !inPinnedWorkspace())) { mLastSurfaceInsets.set(mAttrs.surfaceInsets); setDragResizing(); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index fa353367c6e4..b945cf15fe1c 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -602,6 +602,22 @@ class WindowStateAnimator { } } + void resetDrawState() { + mDrawState = DRAW_PENDING; + + if (mWin.mAppToken == null) { + return; + } + + if (mWin.mAppToken.mAppAnimator.animation == null) { + mWin.mAppToken.clearAllDrawn(); + } else { + // Currently animating, persist current state of allDrawn until animation + // is complete. + mWin.mAppToken.deferClearAllDrawn = true; + } + } + WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) { final WindowState w = mWin; if (w.restoreSavedSurface()) { @@ -619,16 +635,7 @@ class WindowStateAnimator { if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING"); - mDrawState = DRAW_PENDING; - if (w.mAppToken != null) { - if (w.mAppToken.mAppAnimator.animation == null) { - w.mAppToken.clearAllDrawn(); - } else { - // Currently animating, persist current state of allDrawn until animation - // is complete. - w.mAppToken.deferClearAllDrawn = true; - } - } + resetDrawState(); mService.makeWindowFreezingScreenIfNeededLocked(w); @@ -1360,7 +1367,7 @@ class WindowStateAnimator { int posX = mTmpSize.left; int posY = mTmpSize.top; task.mStack.getDimBounds(mTmpStackBounds); - task.mStack.getFinalAnimationSourceBounds(mTmpSourceBounds); + task.mStack.getFinalAnimationSourceHintBounds(mTmpSourceBounds); if (!mTmpSourceBounds.isEmpty()) { // Get the final target stack bounds, if we are not animating, this is just the // current stack bounds diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 86662b94302e..74ecd11d13f4 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -68,12 +68,15 @@ static JavaVM* sJvm; using android::OK; using android::sp; +using android::wp; using android::status_t; using android::String16; using android::hardware::Return; using android::hardware::Void; using android::hardware::hidl_vec; +using android::hardware::hidl_death_recipient; +using android::hidl::base::V1_0::IBase; using android::hardware::gnss::V1_0::IAGnss; using android::hardware::gnss::V1_0::IAGnssCallback; @@ -97,7 +100,18 @@ using android::hardware::gnss::V1_0::IGnssNiCallback; using android::hardware::gnss::V1_0::IGnssXtra; using android::hardware::gnss::V1_0::IGnssXtraCallback; +struct GnssDeathRecipient : virtual public hidl_death_recipient +{ + // hidl_death_recipient interface + virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override { + // TODO(gomo): implement a better death recovery mechanism without + // crashing system server process as described in go//treble-gnss-death + LOG_ALWAYS_FATAL("Abort due to IGNSS hidl service failure," + " restarting system server"); + } +}; +sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr; sp<IGnss> gnssHal = nullptr; sp<IGnssXtra> gnssXtraIface = nullptr; sp<IAGnssRil> agnssRilIface = nullptr; @@ -1038,6 +1052,18 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, // TODO(b/31632518) gnssHal = IGnss::getService(); if (gnssHal != nullptr) { + gnssHalDeathRecipient = new GnssDeathRecipient(); + hardware::Return<bool> linked = gnssHal->linkToDeath( + gnssHalDeathRecipient, /*cookie*/ 0); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to GnssHAL death: %s", + linked.description().c_str()); + } else if (!linked) { + ALOGW("Unable to link to GnssHal death notifications"); + } else { + ALOGD("Link to death notification successful"); + } + auto gnssXtra = gnssHal->getExtensionXtra(); if (!gnssXtra.isOk()) { ALOGD("Unable to get a handle to Xtra"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bfa1b9988b2f..e82ba9cee919 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -742,8 +742,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean forceEphemeralUsers = false; // Can only be set by a device owner. boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner. - // one notification after enabling + 3 more after reboots - static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 4; + // one notification after enabling + one more after reboots + static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2; int numNetworkLoggingNotifications = 0; long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 42a92327bd4e..978803dfb5e0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -159,7 +159,7 @@ public final class SystemServer { private static final String PRINT_MANAGER_SERVICE_CLASS = "com.android.server.print.PrintManagerService"; private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS = - "com.android.server.print.CompanionDeviceManagerService"; + "com.android.server.companion.CompanionDeviceManagerService"; private static final String USB_SERVICE_CLASS = "com.android.server.usb.UsbService$Lifecycle"; private static final String MIDI_SERVICE_CLASS = diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java new file mode 100644 index 000000000000..24cb72e8b0fb --- /dev/null +++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java @@ -0,0 +1,153 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class GlobalSortKeyComparatorTest { + + private final String PKG = "PKG"; + private final int UID = 1111111; + private static final String TEST_CHANNEL_ID = "test_channel_id"; + + @Test + public void testComparator() throws Exception { + Notification n = new Notification.Builder( + InstrumentationRegistry.getContext(), TEST_CHANNEL_ID) + .build(); + NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + left.setGlobalSortKey("first"); + + NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + right.setGlobalSortKey("second"); + + NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + + + final List<NotificationRecord> expected = new ArrayList<>(); + expected.add(left); + expected.add(right); + expected.add(last); + + List<NotificationRecord> actual = new ArrayList<>(); + actual.addAll(expected); + Collections.shuffle(actual); + + Collections.sort(actual, new GlobalSortKeyComparator()); + + assertEquals(expected, actual); + } + + @Test + public void testNoCrash_leftNull() throws Exception { + Notification n = new Notification.Builder( + InstrumentationRegistry.getContext(), TEST_CHANNEL_ID) + .build(); + NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + + NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + right.setGlobalSortKey("not null"); + + final List<NotificationRecord> expected = new ArrayList<>(); + expected.add(right); + expected.add(left); + + List<NotificationRecord> actual = new ArrayList<>(); + actual.addAll(expected); + Collections.shuffle(actual); + + Collections.sort(actual, new GlobalSortKeyComparator()); + + assertEquals(expected, actual); + } + + @Test + public void testNoCrash_rightNull() throws Exception { + Notification n = new Notification.Builder( + InstrumentationRegistry.getContext(), TEST_CHANNEL_ID) + .build(); + NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + left.setGlobalSortKey("not null"); + + NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(), + new StatusBarNotification(PKG, + PKG, 1, "media", UID, UID, n, + new UserHandle(UserHandle.myUserId()), + "", 1499), getDefaultChannel()); + + final List<NotificationRecord> expected = new ArrayList<>(); + expected.add(left); + expected.add(right); + + List<NotificationRecord> actual = new ArrayList<>(); + actual.addAll(expected); + Collections.shuffle(actual); + + Collections.sort(actual, new GlobalSortKeyComparator()); + + assertEquals(expected, actual); + } + + private NotificationChannel getDefaultChannel() { + return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", + NotificationManager.IMPORTANCE_LOW); + } +} diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index f666727ec1d1..57ee928f7682 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -49,6 +49,7 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.graphics.Color; import android.os.Binder; +import android.os.Process; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -460,10 +461,10 @@ public class NotificationManagerServiceTest { mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel, channel2))); verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG), - eq(mTestNotificationChannel), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED)); verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG), - eq(channel2), + eq(Process.myUserHandle()), eq(channel2), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED)); } @@ -481,10 +482,10 @@ public class NotificationManagerServiceTest { mBinderService.createNotificationChannelGroups(PKG, new ParceledListSlice(Arrays.asList(group1, group2))); verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG), - eq(group1), + eq(Process.myUserHandle()), eq(group1), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED)); verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG), - eq(group2), + eq(Process.myUserHandle()), eq(group2), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED)); } @@ -503,7 +504,7 @@ public class NotificationManagerServiceTest { reset(mNotificationListeners); mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel); verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG), - eq(mTestNotificationChannel), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); } @@ -520,7 +521,7 @@ public class NotificationManagerServiceTest { reset(mNotificationListeners); mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId()); verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG), - eq(mTestNotificationChannel), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED)); } @@ -537,7 +538,7 @@ public class NotificationManagerServiceTest { reset(mNotificationListeners); mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId()); verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG), - eq(ncg), + eq(Process.myUserHandle()), eq(ncg), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED)); } @@ -550,12 +551,12 @@ public class NotificationManagerServiceTest { when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); mBinderService.updateNotificationChannelFromPrivilegedListener( - null, PKG, mTestNotificationChannel); + null, PKG, Process.myUserHandle(), mTestNotificationChannel); verify(mRankingHelper, times(1)).updateNotificationChannel(anyString(), anyInt(), any()); verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG), - eq(mTestNotificationChannel), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); } @@ -568,7 +569,7 @@ public class NotificationManagerServiceTest { try { mBinderService.updateNotificationChannelFromPrivilegedListener( - null, PKG, mTestNotificationChannel); + null, PKG, Process.myUserHandle(), mTestNotificationChannel); fail("listeners that don't have a companion device shouldn't be able to call this"); } catch (SecurityException e) { // pass @@ -577,7 +578,33 @@ public class NotificationManagerServiceTest { verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any()); verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG), - eq(mTestNotificationChannel), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), + eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); + } + + @Test + @UiThreadTest + public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception { + mNotificationManagerService.setRankingHelper(mRankingHelper); + List<String> associations = new ArrayList<>(); + associations.add("a"); + when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); + mListener = mock(ManagedServices.ManagedServiceInfo.class); + when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false); + when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); + + try { + mBinderService.updateNotificationChannelFromPrivilegedListener( + null, PKG, UserHandle.ALL, mTestNotificationChannel); + fail("incorrectly allowed a change to a user listener cannot see"); + } catch (SecurityException e) { + // pass + } + + verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any()); + + verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG), + eq(Process.myUserHandle()), eq(mTestNotificationChannel), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); } @@ -589,7 +616,8 @@ public class NotificationManagerServiceTest { associations.add("a"); when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); - mBinderService.getNotificationChannelsFromPrivilegedListener(null, PKG); + mBinderService.getNotificationChannelsFromPrivilegedListener( + null, PKG, Process.myUserHandle()); verify(mRankingHelper, times(1)).getNotificationChannels( anyString(), anyInt(), anyBoolean()); @@ -603,7 +631,8 @@ public class NotificationManagerServiceTest { when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); try { - mBinderService.getNotificationChannelsFromPrivilegedListener(null, PKG); + mBinderService.getNotificationChannelsFromPrivilegedListener( + null, PKG, Process.myUserHandle()); fail("listeners that don't have a companion device shouldn't be able to call this"); } catch (SecurityException e) { // pass @@ -615,13 +644,37 @@ public class NotificationManagerServiceTest { @Test @UiThreadTest + public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception { + mNotificationManagerService.setRankingHelper(mRankingHelper); + List<String> associations = new ArrayList<>(); + associations.add("a"); + when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); + mListener = mock(ManagedServices.ManagedServiceInfo.class); + when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false); + when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); + + try { + mBinderService.getNotificationChannelsFromPrivilegedListener( + null, PKG, Process.myUserHandle()); + fail("listener getting channels from a user they cannot see"); + } catch (SecurityException e) { + // pass + } + + verify(mRankingHelper, never()).getNotificationChannels( + anyString(), anyInt(), anyBoolean()); + } + + @Test + @UiThreadTest public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception { mNotificationManagerService.setRankingHelper(mRankingHelper); List<String> associations = new ArrayList<>(); associations.add("a"); when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); - mBinderService.getNotificationChannelGroupsFromPrivilegedListener(null, PKG); + mBinderService.getNotificationChannelGroupsFromPrivilegedListener( + null, PKG, Process.myUserHandle()); verify(mRankingHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt()); } @@ -634,7 +687,29 @@ public class NotificationManagerServiceTest { when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); try { - mBinderService.getNotificationChannelGroupsFromPrivilegedListener(null, PKG); + mBinderService.getNotificationChannelGroupsFromPrivilegedListener( + null, PKG, Process.myUserHandle()); + fail("listeners that don't have a companion device shouldn't be able to call this"); + } catch (SecurityException e) { + // pass + } + + verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt()); + } + + @Test + @UiThreadTest + public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception { + mNotificationManagerService.setRankingHelper(mRankingHelper); + List<String> associations = new ArrayList<>(); + when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations); + mListener = mock(ManagedServices.ManagedServiceInfo.class); + when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false); + when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener); + + try { + mBinderService.getNotificationChannelGroupsFromPrivilegedListener( + null, PKG, Process.myUserHandle()); fail("listeners that don't have a companion device shouldn't be able to call this"); } catch (SecurityException e) { // pass diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java new file mode 100644 index 000000000000..362c47ad34a4 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static junit.framework.Assert.*; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import junit.framework.Assert; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BootReceiverFixFsckFsStatTest { + + private static final String PARTITION = "userdata"; + + @Test + public void testTreeOptimization() { + final String[] logs = { + "e2fsck 1.43.3 (04-Sep-2016)", + "Pass 1: Checking inodes, blocks, and sizes", + "Inode 877141 extent tree (at level 1) could be shorter. Fix? yes", + " ", + "Pass 1E: Optimizing extent trees", + "Pass 2: Checking directory structure", + "Pass 3: Checking directory connectivity", + "Pass 4: Checking reference counts", + "Pass 5: Checking group summary information", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)", + "Update quota info for quota type 0? yes", + " ", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)", + "Update quota info for quota type 1? yes", + " ", + "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****" + }; + doTestFsckFsStat(logs, 0x405, 5, 0, logs.length); + + final String[] doubleLogs = new String[logs.length * 2]; + System.arraycopy(logs, 0, doubleLogs, 0, logs.length); + System.arraycopy(logs, 0, doubleLogs, logs.length, logs.length); + doTestFsckFsStat(doubleLogs, 0x401, 1, 0, logs.length); + doTestFsckFsStat(doubleLogs, 0x402, 2, logs.length, logs.length * 2); + } + + @Test + public void testQuotaOnly() { + final String[] logs = { + "e2fsck 1.43.3 (04-Sep-2016)", + "Pass 1: Checking inodes, blocks, and sizes", + "Pass 1E: Optimizing extent trees", + "Pass 2: Checking directory structure", + "Pass 3: Checking directory connectivity", + "Pass 4: Checking reference counts", + "Pass 5: Checking group summary information", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)", + "Update quota info for quota type 0? yes", + " ", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)", + "Update quota info for quota type 1? yes", + " ", + "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****" + }; + doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length); + } + + @Test + public void testOrphaned() { + final String[] logs = { + "e2fsck 1.43.3 (04-Sep-2016)", + "Pass 1: Checking inodes, blocks, and sizes", + "Inodes that were part of a corrupted orphan linked list found. Fix? yes", + " ", + "Inode 589877 was part of the orphaned inode list. FIXED.", + " ", + "Inode 589878 was part of the orphaned inode list. FIXED.", + " ", + "Pass 2: Checking directory structure", + "Pass 3: Checking directory connectivity", + "Pass 4: Checking reference counts", + "Pass 5: Checking group summary information", + " ", + "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****" + }; + doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length); + } + + private void doTestFsckFsStat(String[] lines, int statOrg, int statUpdated, int startLineNumber, + int endLineNumber) { + assertEquals(statUpdated, BootReceiver.fixFsckFsStat(PARTITION, statOrg, lines, + startLineNumber, endLineNumber)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index 54ecab3af542..f75d49cae574 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -16,7 +16,8 @@ package com.android.server.am; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import android.content.ComponentName; import android.platform.test.annotations.Presubmit; @@ -36,50 +37,61 @@ import org.junit.Test; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityRecordTests extends ActivityTestsBase { + private static final int TEST_STACK_ID = 100; + private final ComponentName testActivityComponent = ComponentName.unflattenFromString("com.foo/.BarActivity"); @Test public void testStackCleanupOnClearingTask() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, task); record.setTask(null); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1); } @Test public void testStackCleanupOnActivityRemoval() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, task); task.removeActivity(record); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1); } @Test public void testStackCleanupOnTaskRemoval() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, task); - testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + service.mStackSupervisor.getStack(TEST_STACK_ID) + .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); + + // Stack should be gone on task removal. + assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID)); } @Test public void testNoCleanupMovingActivityInSameStack() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord oldTask = createTask(service, testActivityComponent, testStack); + final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord record = createActivity(service, testActivityComponent, oldTask); - final TaskRecord newTask = createTask(service, testActivityComponent, testStack); + final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID); record.reparent(newTask, 0, null /*reason*/); - assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0); + assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0); + } + + private static int getActivityRemovedFromStackCount(ActivityManagerService service, + int stackId) { + final ActivityStack stack = service.mStackSupervisor.getStack(stackId); + if (stack instanceof ActivityStackReporter) { + return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount(); + } + + return -1; } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java index 8423affecd30..fc9ab9635c57 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -16,8 +16,14 @@ package com.android.server.am; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import android.content.ComponentName; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; @@ -25,6 +31,10 @@ import android.support.test.runner.AndroidJUnit4; import org.junit.runner.RunWith; import org.junit.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; /** @@ -37,6 +47,9 @@ import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackSupervisorTests extends ActivityTestsBase { + private final ComponentName testActivityComponent = + ComponentName.unflattenFromString("com.foo/.BarActivity"); + /** * This test ensures that we do not try to restore a task based off an invalid task id. The * stack supervisor is a test version so there will be no tasks present. We should expect @@ -49,4 +62,59 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/); assertNull(task); } + + /** + * This test ensures that an existing task in the pinned stack is moved to the fullscreen + * activity stack when a new task is added. + */ + @Test + public void testReplacingTaskInPinnedStack() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TaskRecord firstTask = createTask(service, testActivityComponent, + FULLSCREEN_WORKSPACE_STACK_ID); + final ActivityRecord firstActivity = createActivity(service, testActivityComponent, + firstTask); + // Create a new task on the full screen stack + final TaskRecord secondTask = createTask(service, testActivityComponent, + FULLSCREEN_WORKSPACE_STACK_ID); + final ActivityRecord secondActivity = createActivity(service, testActivityComponent, + secondTask); + service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", + service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID)); + + // Ensure full screen stack has both tasks. + ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask, + secondTask); + + // Move first activity to pinned stack. + service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity, + new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove"); + + // Ensure a task has moved over. + ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask); + ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask); + + // Move second activity to pinned stack. + service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity, + new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove"); + + // Ensure stacks have swapped tasks. + ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask); + ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask); + } + + private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId, + TaskRecord... tasks) { + final ActivityStack stack = supervisor.getStack(stackId); + final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); + assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0); + + if (tasks == null) { + return; + } + + for (TaskRecord task : tasks) { + assertTrue(stackTasks.contains(task)); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 1d80b336becc..f42abf1927f0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -37,28 +37,27 @@ import org.junit.Test; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackTests extends ActivityTestsBase { - private final ComponentName testActivityComponent = + private static final int TEST_STACK_ID = 100; + private static final ComponentName testActivityComponent = ComponentName.unflattenFromString("com.foo/.BarActivity"); @Test public void testEmptyTaskCleanupOnRemove() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); assertNotNull(task.getWindowContainerController()); - testStack.removeTask(task, "testEmptyTaskCleanupOnRemove", - ActivityStack.REMOVE_TASK_MODE_DESTROYING); + service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task, + "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING); assertNull(task.getWindowContainerController()); } @Test public void testOccupiedTaskCleanupOnRemove() throws Exception { final ActivityManagerService service = createActivityManagerService(); - final TestActivityStack testStack = new ActivityStackBuilder(service).build(); - final TaskRecord task = createTask(service, testActivityComponent, testStack); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task); assertNotNull(task.getWindowContainerController()); - testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove", - ActivityStack.REMOVE_TASK_MODE_DESTROYING); + service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task, + "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING); assertNotNull(task.getWindowContainerController()); } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 3bf0e5ff86aa..082708442032 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -17,6 +17,11 @@ package com.android.server.am; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; + +import org.mockito.invocation.InvocationOnMock; import android.app.ActivityManager; import android.content.ComponentName; @@ -33,6 +38,7 @@ import com.android.server.AttributeCache; import com.android.server.wm.AppWindowContainerController; import com.android.server.wm.StackWindowController; +import com.android.server.wm.TaskWindowContainerController; import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowTestUtils; import org.junit.After; @@ -64,16 +70,15 @@ public class ActivityTestsBase { protected ActivityManagerService createActivityManagerService() { final ActivityManagerService service = new TestActivityManagerService(mContext); - service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext); + service.mWindowManager = WindowTestUtils.getMockWindowManagerService(); return service; } - protected static TestActivityStack createActivityStack(ActivityManagerService service, + protected static ActivityStack createActivityStack(ActivityManagerService service, int stackId, int displayId, boolean onTop) { if (service.mStackSupervisor instanceof TestActivityStackSupervisor) { - final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor) + return ((TestActivityStackSupervisor) service.mStackSupervisor) .createTestStack(service, stackId, onTop); - return stack; } return null; @@ -103,7 +108,7 @@ public class ActivityTestsBase { } protected static TaskRecord createTask(ActivityManagerService service, - ComponentName component, ActivityStack stack) { + ComponentName component, int stackId) { final ActivityInfo aInfo = new ActivityInfo(); aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = component.getPackageName(); @@ -113,13 +118,16 @@ public class ActivityTestsBase { final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/, null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo()); + final ActivityStack stack = service.mStackSupervisor.getStack(stackId, + true /*createStaticStackIfNeeded*/, true /*onTop*/); stack.addTask(task, true, "creating test task"); task.setStack(stack); - task.createWindowContainer(true, true); + task.setWindowContainerController(mock(TaskWindowContainerController.class)); return task; } + /** * An {@link ActivityManagerService} subclass which provides a test * {@link ActivityStackSupervisor}. @@ -127,6 +135,9 @@ public class ActivityTestsBase { protected static class TestActivityManagerService extends ActivityManagerService { public TestActivityManagerService(Context context) { super(context); + mSupportsMultiWindow = true; + mSupportsMultiDisplay = true; + mWindowManager = WindowTestUtils.getWindowManagerService(context); } @Override @@ -142,6 +153,12 @@ public class ActivityTestsBase { protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { super(service, looper); + mWindowManager = prepareMockWindowManager(); + } + + // No home stack is set. + @Override + void moveHomeStackToFront(String reason) { } // Invoked during {@link ActivityStack} creation. @@ -149,18 +166,45 @@ public class ActivityTestsBase { void updateUIDsPresentOnDisplay() { } - public TestActivityStack createTestStack(ActivityManagerService service, int stackId, - boolean onTop) { + // Just return the current front task. + @Override + ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) { + return mFocusedStack; + } + + // Called when moving activity to pinned stack. + @Override + void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, + boolean preserveWindows) { + } + + public <T extends ActivityStack> T createTestStack(ActivityManagerService service, + int stackId, boolean onTop) { final ActivityDisplay display = new ActivityDisplay(); final TestActivityContainer container = new TestActivityContainer(service, stackId, display, onTop); - return container.getStack(); + mActivityContainers.put(stackId, container); + return (T) container.getStack(); + } + + @Override + protected <T extends ActivityStack> T getStack(int stackId, + boolean createStaticStackIfNeeded, boolean createOnTop) { + final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop); + + if (stack != null || !createStaticStackIfNeeded) { + return stack; + } + + return createTestStack(mService, stackId, createOnTop); } private class TestActivityContainer extends ActivityContainer { - private ActivityManagerService mService; - private TestActivityStack mStack; + private final ActivityManagerService mService; + private boolean mOnTop; + private int mStackId; + private ActivityStack mStack; TestActivityContainer(ActivityManagerService service, int stackId, ActivityDisplay activityDisplay, boolean onTop) { @@ -174,12 +218,16 @@ public class ActivityTestsBase { // we cannot set {@link mService} by the time the super constructor calling this // method is invoked. mOnTop = onTop; + mStackId = stackId; } - public TestActivityStack getStack() { + public ActivityStack getStack() { if (mStack == null) { - mStack = new TestActivityStack(this, - new RecentTasks(mService, mService.mStackSupervisor), mOnTop); + final RecentTasks recents = + new RecentTasks(mService, mService.mStackSupervisor); + mStack = mStackId == ActivityManager.StackId.PINNED_STACK_ID + ? new PinnedActivityStack(this, recents, mOnTop) + : new TestActivityStack(this, recents, mOnTop); } return mStack; @@ -187,13 +235,31 @@ public class ActivityTestsBase { } } + private static WindowManagerService prepareMockWindowManager() { + final WindowManagerService service = mock(WindowManagerService.class); + + doAnswer((InvocationOnMock invocationOnMock) -> { + final Runnable runnable = invocationOnMock.<Runnable>getArgument(0); + if (runnable != null) { + runnable.run(); + } + return null; + }).when(service).inSurfaceTransaction(any()); + + return service; + } + + protected interface ActivityStackReporter { + int onActivityRemovedFromStackInvocationCount(); + } + /** * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a * method is called. Note that its functionality depends on the implementations of the * construction arguments. */ protected static class TestActivityStack<T extends StackWindowController> - extends ActivityStack<T> { + extends ActivityStack<T> implements ActivityStackReporter { private int mOnActivityRemovedFromStackCount = 0; private T mContainerController; TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer, @@ -208,6 +274,7 @@ public class ActivityTestsBase { } // Returns the number of times {@link #onActivityRemovedFromStack} has been called + @Override public int onActivityRemovedFromStackInvocationCount() { return mOnActivityRemovedFromStackCount; } @@ -225,6 +292,7 @@ public class ActivityTestsBase { } } + protected static class ActivityStackBuilder { private boolean mOnTop = true; private int mStackId = 0; @@ -251,7 +319,7 @@ public class ActivityTestsBase { return this; } - public TestActivityStack build() { + public ActivityStack build() { return createActivityStack(mService, mStackId, mDisplayId, mOnTop); } } diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java new file mode 100644 index 000000000000..243c1b379608 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.content.Context; +import android.os.Handler; +import android.support.test.InstrumentationRegistry; +import android.support.test.annotation.UiThreadTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.AppOpsService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +/** + * runtest -c com.android.server.am.AppErrorDialogTest frameworks-services + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class AppErrorDialogTest { + + private Context mContext; + private ActivityManagerService mService; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + mService = new ActivityManagerService(new ActivityManagerService.Injector() { + @Override + public AppOpsService getAppOpsService(File file, Handler handler) { + return null; + } + + @Override + public Handler getUiHandler(ActivityManagerService service) { + return null; + } + + @Override + public boolean isNetworkRestrictedForUid(int uid) { + return false; + } + }); + } + + @Test + @UiThreadTest + public void testCreateWorks() throws Exception { + AppErrorDialog.Data data = new AppErrorDialog.Data(); + data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345); + data.result = new AppErrorResult(); + + AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data); + + dialog.create(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java new file mode 100644 index 000000000000..8f2f34ee77d9 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.IIntentReceiver; + +import android.os.Bundle; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import java.io.File; + +// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services + +@SmallTest +public class PackageManagerServiceTest extends AndroidTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testPackageRemoval() throws Exception { + class PackageSenderImpl implements PackageSender { + public void sendPackageBroadcast(final String action, final String pkg, + final Bundle extras, final int flags, final String targetPkg, + final IIntentReceiver finishedReceiver, final int[] userIds) { + } + + public void sendPackageAddedForNewUsers(String packageName, + boolean isSystem, int appId, int... userIds) { + } + } + + PackageSenderImpl sender = new PackageSenderImpl(); + PackageSetting setting = null; + PackageManagerService.PackageRemovedInfo pri = + new PackageManagerService.PackageRemovedInfo(sender); + + // Initial conditions: nothing there + assertNull(pri.removedUsers); + assertNull(pri.broadcastUsers); + + // populateUsers with nothing leaves nothing + pri.populateUsers(null, setting); + assertNull(pri.broadcastUsers); + + // Create a real (non-null) PackageSetting and confirm that the removed + // users are copied properly + setting = new PackageSetting("name", "realName", new File("codePath"), + new File("resourcePath"), "legacyNativeLibraryPathString", + "primaryCpuAbiString", "secondaryCpuAbiString", + "cpuAbiOverrideString", 0, 0, 0, "parentPackageName", null, 0, + null, null); + pri.populateUsers(new int[] {1, 2, 3, 4, 5}, setting); + assertNotNull(pri.broadcastUsers); + assertEquals(5, pri.broadcastUsers.length); + + // Exclude a user + pri.broadcastUsers = null; + final int EXCLUDED_USER_ID = 4; + setting.setInstantApp(true, EXCLUDED_USER_ID); + pri.populateUsers(new int[] {1, 2, 3, EXCLUDED_USER_ID, 5}, setting); + assertNotNull(pri.broadcastUsers); + assertEquals(5 - 1, pri.broadcastUsers.length); + + // TODO: test that sendApplicationHiddenForUser() actually fills in + // broadcastUsers + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java index 929a73d805c0..c314de4f13eb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java @@ -27,6 +27,7 @@ import android.os.UserManagerInternal; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; +import android.util.IconDrawableFactory; import com.android.server.LocalServices; @@ -155,9 +156,9 @@ public class UserManagerServiceCreateProfileTest { public void testNumberOfBadges() { assertTrue("Max profiles greater than number of badges", UserManagerService.MAX_MANAGED_PROFILES - <= ApplicationPackageManager.CORP_BADGE_COLORS.length); + <= IconDrawableFactory.CORP_BADGE_COLORS.length); assertEquals("Num colors doesn't match number of badge labels", - ApplicationPackageManager.CORP_BADGE_COLORS.length, + IconDrawableFactory.CORP_BADGE_COLORS.length, ApplicationPackageManager.CORP_BADGE_LABEL_RES_ID.length); } diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 85dc7125e3cc..cd7a7c7090fc 100644 --- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -16,6 +16,11 @@ package com.android.server.wm; +import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; +import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; + import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Rect; @@ -47,6 +52,12 @@ import com.android.server.wm.BoundsAnimationController.BoundsAnimator; * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks * depending on the various interactions. * + * We are really concerned about only three of the transition states [F = fullscreen, !F = floating] + * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition, + * or if a new animation starts on the same target. The tests below verifies that the target is + * notified of all the cases where it is animating and cancelled so that it can respond + * appropriately. + * * Build/Install/Run: * bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests */ @@ -109,47 +120,48 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { /** * A test animate bounds user to track callbacks from the bounds animation. */ - private class TestAnimateBoundsUser implements BoundsAnimationController.AnimateBoundsUser { + private class TestBoundsAnimationTarget implements BoundsAnimationTarget { + boolean mAwaitingAnimationStart; boolean mMovedToFullscreen; boolean mAnimationStarted; - boolean mAnimationStartedToFullscreen; + boolean mSchedulePipModeChangedOnStart; boolean mAnimationEnded; - boolean mUpdatedPictureInPictureModeWithBounds; + Rect mAnimationEndFinalStackBounds; + boolean mSchedulePipModeChangedOnEnd; boolean mBoundsUpdated; + boolean mCancelRequested; Rect mStackBounds; Rect mTaskBounds; - boolean mRequestCancelAnimation = false; - - void reinitialize(Rect stackBounds, Rect taskBounds) { + void initialize(Rect from) { + mAwaitingAnimationStart = true; mMovedToFullscreen = false; mAnimationStarted = false; - mAnimationStartedToFullscreen = false; mAnimationEnded = false; - mUpdatedPictureInPictureModeWithBounds = false; - mStackBounds = stackBounds; - mTaskBounds = taskBounds; + mAnimationEndFinalStackBounds = null; + mSchedulePipModeChangedOnStart = false; + mSchedulePipModeChangedOnEnd = false; + mStackBounds = from; + mTaskBounds = null; mBoundsUpdated = false; - mRequestCancelAnimation = false; } @Override - public void onAnimationStart(boolean toFullscreen) { + public void onAnimationStart(boolean schedulePipModeChangedCallback) { + mAwaitingAnimationStart = false; mAnimationStarted = true; - mAnimationStartedToFullscreen = toFullscreen; - } + mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; - @Override - public void updatePictureInPictureMode(Rect targetStackBounds) { - mUpdatedPictureInPictureModeWithBounds = true; + mController.onAllWindowsDrawn(); } @Override public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { // TODO: Once we break the runs apart, we should fail() here if this is called outside // of onAnimationStart() and onAnimationEnd() - if (mRequestCancelAnimation) { + if (mCancelRequested) { + mCancelRequested = false; return false; } else { mBoundsUpdated = true; @@ -160,32 +172,206 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { } @Override - public void onAnimationEnd() { + public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds, + boolean moveToFullscreen) { mAnimationEnded = true; + mAnimationEndFinalStackBounds = finalStackBounds; + mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback; + mMovedToFullscreen = moveToFullscreen; + mTaskBounds = null; } + } - @Override - public void moveToFullscreen() { - mMovedToFullscreen = true; + /** + * Drives the animations, makes common assertions along the way. + */ + private class BoundsAnimationDriver { + + private BoundsAnimationController mController; + private TestBoundsAnimationTarget mTarget; + private BoundsAnimator mAnimator; + + private Rect mFrom; + private Rect mTo; + private Rect mLargerBounds; + private Rect mExpectedFinalBounds; + + BoundsAnimationDriver(BoundsAnimationController controller, + TestBoundsAnimationTarget target) { + mController = controller; + mTarget = target; + } + + BoundsAnimationDriver start(Rect from, Rect to) { + if (mAnimator != null) { + throw new IllegalArgumentException("Call restart() to restart an animation"); + } + + mTarget.initialize(from); + + // Started, not running + assertTrue(mTarget.mAwaitingAnimationStart); + assertTrue(!mTarget.mAnimationStarted); + + startImpl(from, to); + + // Started and running + assertTrue(!mTarget.mAwaitingAnimationStart); + assertTrue(mTarget.mAnimationStarted); + + return this; + } + + BoundsAnimationDriver restart(Rect to) { + if (mAnimator == null) { + throw new IllegalArgumentException("Call start() to start a new animation"); + } + + BoundsAnimator oldAnimator = mAnimator; + boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo); + + // Reset the animation start state + mTarget.mAnimationStarted = false; + + // Start animation + startImpl(mTarget.mStackBounds, to); + + if (toSameBounds) { + // Same animator if same final bounds + assertSame(oldAnimator, mAnimator); + } + + // No animation start for replacing animation + assertTrue(!mTarget.mAnimationStarted); + mTarget.mAnimationStarted = true; + return this; + } + + private BoundsAnimationDriver startImpl(Rect from, Rect to) { + boolean fromFullscreen = from.equals(BOUNDS_FULL); + boolean toFullscreen = to.equals(BOUNDS_FULL); + mFrom = new Rect(from); + mTo = new Rect(to); + mExpectedFinalBounds = new Rect(to); + mLargerBounds = getLargerBounds(mFrom, mTo); + + // Start animation + final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen + ? SCHEDULE_PIP_MODE_CHANGED_ON_START + : fromFullscreen + ? SCHEDULE_PIP_MODE_CHANGED_ON_END + : NO_PIP_MODE_CHANGED_CALLBACKS; + mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, + schedulePipModeChangedState, toFullscreen); + + // Original stack bounds, frozen task bounds + assertEquals(mFrom, mTarget.mStackBounds); + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + + // Animating to larger size + if (mFrom.equals(mLargerBounds)) { + assertTrue(!mAnimator.animatingToLargerSize()); + } else if (mTo.equals(mLargerBounds)) { + assertTrue(mAnimator.animatingToLargerSize()); + } + + return this; + } + + BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) { + // Callback made + assertTrue(mTarget.mAnimationStarted); + + assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart); + return this; + } + + BoundsAnimationDriver update(float t) { + mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); + + // Temporary stack bounds, frozen task bounds + if (t == 0f) { + assertEquals(mFrom, mTarget.mStackBounds); + } else if (t == 1f) { + assertEquals(mTo, mTarget.mStackBounds); + } else { + assertNotEquals(mFrom, mTarget.mStackBounds); + assertNotEquals(mTo, mTarget.mStackBounds); + } + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + return this; + } + + BoundsAnimationDriver cancel() { + // Cancel + mTarget.mCancelRequested = true; + mTarget.mBoundsUpdated = false; + mExpectedFinalBounds = null; + + // Update + mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f)); + + // Not started, not running, cancel reset + assertTrue(!mTarget.mCancelRequested); + + // Stack/task bounds not updated + assertTrue(!mTarget.mBoundsUpdated); + + // Callback made + assertTrue(mTarget.mAnimationEnded); + assertNull(mTarget.mAnimationEndFinalStackBounds); + + return this; + } + + BoundsAnimationDriver end() { + mAnimator.end(); + + // Final stack bounds + assertEquals(mTo, mTarget.mStackBounds); + assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); + assertNull(mTarget.mTaskBounds); + + return this; + } + + BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged, + boolean moveToFullscreen) { + // Callback made + assertTrue(mTarget.mAnimationEnded); + + assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd); + assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen); + return this; + } + + private Rect getLargerBounds(Rect r1, Rect r2) { + int r1Area = r1.width() * r1.height(); + int r2Area = r2.width() * r2.height(); + if (r1Area <= r2Area) { + return r2; + } else { + return r1; + } } } // Constants + private static final boolean SCHEDULE_PIP_MODE_CHANGED = true; private static final boolean MOVE_TO_FULLSCREEN = true; + private static final int DURATION = 100; // Some dummy bounds to represent fullscreen and floating bounds private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100); - private static final Rect BOUNDS_FLOATING = new Rect(80, 80, 95, 95); - private static final Rect BOUNDS_ALT_FLOATING = new Rect(60, 60, 95, 95); - - // Some dummy duration - private static final int DURATION = 100; + private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95); + private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95); // Common - private MockAppTransition mAppTransition; - private MockValueAnimator mAnimator; - private TestAnimateBoundsUser mTarget; + private MockAppTransition mMockAppTransition; + private MockValueAnimator mMockAnimator; + private TestBoundsAnimationTarget mTarget; private BoundsAnimationController mController; + private BoundsAnimationDriver mDriver; // Temp private Rect mTmpRect = new Rect(); @@ -196,153 +382,184 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { final Context context = InstrumentationRegistry.getTargetContext(); final Handler handler = new Handler(Looper.getMainLooper()); - mAppTransition = new MockAppTransition(context); - mAnimator = new MockValueAnimator(); - mTarget = new TestAnimateBoundsUser(); - mController = new BoundsAnimationController(context, mAppTransition, handler); + mMockAppTransition = new MockAppTransition(context); + mMockAnimator = new MockValueAnimator(); + mTarget = new TestBoundsAnimationTarget(); + mController = new BoundsAnimationController(context, mMockAppTransition, handler); + mDriver = new BoundsAnimationDriver(mController, mTarget); } + /** BASE TRANSITIONS **/ + @UiThreadTest @Test public void testFullscreenToFloatingTransition() throws Exception { - // Create and start the animation - mTarget.reinitialize(BOUNDS_FULL, null); - final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL, - BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN); - - // Assert that when we are started, and that we are not going to fullscreen - assertTrue(mTarget.mAnimationStarted); - assertFalse(mTarget.mAnimationStartedToFullscreen); - // Ensure we are not triggering a PiP mode change - assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds); - // Ensure that the task stack bounds are already frozen to the larger source stack bounds - assertEquals(BOUNDS_FULL, mTarget.mStackBounds); - assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds)); - - // Drive some animation updates, ensure that only the stack bounds change and the task - // bounds are frozen to the original stack bounds (adjusted for the offset) - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f)); - assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds); - assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds)); - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f)); - assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds); - assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds)); - - // Finish the animation, ensure that it reaches the final bounds with the given state - boundsAnimator.end(); - assertTrue(mTarget.mAnimationEnded); - assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds); - assertNull(mTarget.mTaskBounds); + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); } @UiThreadTest @Test public void testFloatingToFullscreenTransition() throws Exception { - // Create and start the animation - mTarget.reinitialize(BOUNDS_FULL, null); - final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FLOATING, - BOUNDS_FULL, DURATION, MOVE_TO_FULLSCREEN); - - // Assert that when we are started, and that we are going to fullscreen - assertTrue(mTarget.mAnimationStarted); - assertTrue(mTarget.mAnimationStartedToFullscreen); - // Ensure that we update the PiP mode change with the new fullscreen bounds - assertTrue(mTarget.mUpdatedPictureInPictureModeWithBounds); - // Ensure that the task stack bounds are already frozen to the larger target stack bounds - assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds); - assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds)); - - // Drive some animation updates, ensure that only the stack bounds change and the task - // bounds are frozen to the original stack bounds (adjusted for the offset) - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f)); - assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds); - assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds)); - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f)); - assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds); - assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds)); - - // Finish the animation, ensure that it reaches the final bounds with the given state - boundsAnimator.end(); - assertTrue(mTarget.mAnimationEnded); - assertEquals(BOUNDS_FULL, mTarget.mStackBounds); - assertNull(mTarget.mTaskBounds); + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToSmallerFloatingTransition() throws Exception { + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToLargerFloatingTransition() throws Exception { + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + /** F->!F w/ CANCEL **/ + + @UiThreadTest + @Test + public void testFullscreenToFloatingCancelFromTarget() throws Exception { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); } @UiThreadTest @Test - public void testInterruptAnimationFromUser() throws Exception { - // Create and start the animation - mTarget.reinitialize(BOUNDS_FULL, null); - final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL, - BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN); - - // Cancel the animation on the next update from the user - mTarget.mRequestCancelAnimation = true; - mTarget.mBoundsUpdated = false; - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f)); - // Ensure that we got no more updates after returning false and the bounds are not updated - // to the end value - assertFalse(mTarget.mBoundsUpdated); - assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds); - assertNotEquals(BOUNDS_FLOATING, mTarget.mTaskBounds); - // Ensure that we received the animation end call - assertTrue(mTarget.mAnimationEnded); + public void testFullscreenToFloatingCancelFromAnimationToSameBounds() throws Exception { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_FLOATING) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); } @UiThreadTest @Test - public void testCancelAnimationFromNewAnimationToExistingBounds() throws Exception { - // Create and start the animation - mTarget.reinitialize(BOUNDS_FULL, null); - final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL, - BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN); - - // Drive some animation updates - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f)); - - // Cancel the animation as a restart to the same bounds - mTarget.reinitialize(null, null); - final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL, - BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN); - // Ensure the animator is the same - assertSame(boundsAnimator, altBoundsAnimator); - // Ensure we haven't restarted or finished the animation - assertFalse(mTarget.mAnimationStarted); - assertFalse(mTarget.mAnimationEnded); - // Ensure that we haven't tried to update the PiP mode - assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds); + public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() throws Exception { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_SMALLER_FLOATING) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); } @UiThreadTest @Test - public void testCancelAnimationFromNewAnimationToNewBounds() throws Exception { - // Create and start the animation - mTarget.reinitialize(BOUNDS_FULL, null); - final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL, - BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN); - - // Drive some animation updates - boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f)); - - // Cancel the animation as a restart to new bounds - mTarget.reinitialize(null, null); - final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL, - BOUNDS_ALT_FLOATING, DURATION, !MOVE_TO_FULLSCREEN); - // Ensure the animator is not the same - assertNotSame(boundsAnimator, altBoundsAnimator); - // Ensure that we did not get an animation start/end callback - assertFalse(mTarget.mAnimationStarted); - assertFalse(mTarget.mAnimationEnded); - // Ensure that we haven't tried to update the PiP mode - assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds); + public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_FULL) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + /** !F->F w/ CANCEL **/ + + @UiThreadTest + @Test + public void testFloatingToFullscreenCancelFromTarget() throws Exception { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToFullscreenCancelFromAnimationToSameBounds() throws Exception { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_FULL) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() throws Exception { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_SMALLER_FLOATING) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + /** !F->!F w/ CANCEL **/ + + @UiThreadTest + @Test + public void testFloatingToSmallerFloatingCancelFromTarget() throws Exception { + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToLargerFloatingCancelFromTarget() throws Exception { + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + /** MISC **/ + + @UiThreadTest + @Test + public void testBoundsAreCopied() throws Exception { + Rect from = new Rect(0, 0, 100, 100); + Rect to = new Rect(25, 25, 75, 75); + mDriver.start(from, to) + .update(0.25f) + .end(); + assertEquals(new Rect(0, 0, 100, 100), from); + assertEquals(new Rect(25, 25, 75, 75), to); } /** - * @return the bounds offset to zero/zero. + * @return whether the task and stack bounds would be the same if they were at the same offset. */ - private Rect offsetToZero(Rect bounds) { - mTmpRect.set(bounds); - mTmpRect.offsetTo(0, 0); - return mTmpRect; + private boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) { + mTmpRect.set(taskBounds); + mTmpRect.offsetTo(stackBounds.left, stackBounds.top); + return stackBounds.equals(mTmpRect); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index fbeda0a9191c..9392e8ee2b5c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -90,6 +90,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return null; }).when(am).notifyKeyguardFlagsChanged(any()); } + sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false, false, new TestWindowManagerPolicy()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java index 3a443575332e..ae3eb522837f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -48,6 +48,13 @@ public class WindowTestUtils { } /** + * Retrieves an instance of a mock {@link WindowManagerService}. + */ + public static WindowManagerService getMockWindowManagerService() { + return mock(WindowManagerService.class); + } + + /** * Creates a mock instance of {@link StackWindowController}. */ public static StackWindowController createMockStackWindowContainerController() { diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 5ad7f8042c01..094c7bd89ba0 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -35,6 +35,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.ParcelableException; import android.os.StatFs; import android.os.SystemProperties; import android.os.UserHandle; @@ -150,7 +151,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { try { return mInstaller.isQuotaSupported(volumeUuid); } catch (InstallerException e) { - throw new IllegalStateException(e); + throw new ParcelableException(new IOException(e.getMessage())); } } @@ -163,7 +164,8 @@ public class StorageStatsService extends IStorageStatsManager.Stub { } else { final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid); if (vol == null) { - throw new IllegalStateException("Volume was unexpected null"); + throw new ParcelableException( + new IOException("Failed to find storage device for UUID " + volumeUuid)); } return FileUtils.roundStorageSize(vol.disk.size); } @@ -189,7 +191,8 @@ public class StorageStatsService extends IStorageStatsManager.Stub { } else { final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid); if (vol == null) { - throw new IllegalStateException("Volume was unexpected null"); + throw new ParcelableException( + new IOException("Failed to find storage device for UUID " + volumeUuid)); } return vol.getPath().getUsableSpace() + cacheBytes; } @@ -221,7 +224,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { appInfo = mPackage.getApplicationInfoAsUser(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); } catch (NameNotFoundException e) { - throw new IllegalStateException(e); + throw new ParcelableException(e); } if (mPackage.getPackagesForUid(appInfo.uid).length == 1) { @@ -239,7 +242,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { mInstaller.getAppSize(volumeUuid, packageNames, userId, 0, appId, ceDataInodes, codePaths, stats); } catch (InstallerException e) { - throw new IllegalStateException(e); + throw new ParcelableException(new IOException(e.getMessage())); } return translate(stats); } @@ -265,7 +268,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i], PackageManager.MATCH_UNINSTALLED_PACKAGES, userId).getCodePath(); } catch (NameNotFoundException e) { - throw new IllegalStateException(e); + throw new ParcelableException(e); } } @@ -281,7 +284,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { checkEquals("UID " + uid, manualStats, stats); } } catch (InstallerException e) { - throw new IllegalStateException(e); + throw new ParcelableException(new IOException(e.getMessage())); } return translate(stats); } @@ -313,7 +316,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { checkEquals("User " + userId, manualStats, stats); } } catch (InstallerException e) { - throw new IllegalStateException(e); + throw new ParcelableException(new IOException(e.getMessage())); } return translate(stats); } @@ -336,7 +339,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { checkEquals("External " + userId, manualStats, stats); } } catch (InstallerException e) { - throw new IllegalStateException(e); + throw new ParcelableException(new IOException(e.getMessage())); } final ExternalStorageStats res = new ExternalStorageStats(); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 80b73d3677b0..31595a69fc65 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -857,13 +857,16 @@ public class UsbDeviceManager { break; case MSG_UPDATE_HOST_STATE: SomeArgs args = (SomeArgs) msg.obj; + boolean prevHostConnected = mHostConnected; mHostConnected = (args.argi1 == 1); mSourcePower = (args.argi2 == 1); mSinkPower = (args.argi3 == 1); args.recycle(); updateUsbNotification(); if (mBootCompleted) { - updateUsbStateBroadcastIfNeeded(false); + if (mHostConnected || prevHostConnected) { + updateUsbStateBroadcastIfNeeded(false); + } } else { mPendingBootBroadcast = true; } diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java index 943a6cade853..eeaf2c121a75 100644 --- a/telephony/java/android/telephony/Telephony.java +++ b/telephony/java/android/telephony/Telephony.java @@ -19,18 +19,21 @@ package android.provider; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.TestApi; +import android.app.job.JobService; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; +import android.database.ContentObserver; import android.database.sqlite.SqliteWrapper; import android.net.Uri; +import android.telephony.Rlog; +import android.telephony.ServiceState; import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; import android.text.TextUtils; -import android.telephony.Rlog; import android.util.Patterns; import com.android.internal.telephony.PhoneConstants; @@ -44,7 +47,7 @@ import java.util.regex.Pattern; /** * The Telephony provider contains data related to phone operation, specifically SMS and MMS - * messages and access to the APN list, including the MMSC to use. + * messages, access to the APN list, including the MMSC to use, and the service state. * * <p class="note"><strong>Note:</strong> These APIs are not available on all Android-powered * devices. If your app depends on telephony features such as for managing SMS messages, include @@ -2972,4 +2975,272 @@ public final class Telephony { CMAS_CERTAINTY }; } + + /** + * Constants for interfacing with the ServiceStateProvider and the different fields of the + * {@link ServiceState} class accessible through the provider. + */ + public static final class ServiceStateTable { + + /** + * Not instantiable. + * @hide + */ + private ServiceStateTable() {} + + /** + * The authority string for the ServiceStateProvider + */ + public static final String AUTHORITY = "service-state"; + + /** + * The {@code content://} style URL for the ServiceStateProvider + */ + public static final Uri CONTENT_URI = Uri.parse("content://service-state/"); + + /** + * Generates a content {@link Uri} used to receive updates on a specific field in the + * ServiceState provider. + * <p> + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * {@link ServiceState} while your app is running. You can also use a {@link JobService} to + * ensure your app is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link JobService} does not guarantee timely delivery of + * updates to the {@link Uri}. + * + * @param subId the subId to receive updates on + * @param field the ServiceState field to receive updates on + * @return the Uri used to observe {@link ServiceState} changes + */ + public static Uri getUriForSubIdAndField(int subId, String field) { + return CONTENT_URI.buildUpon().appendEncodedPath(String.valueOf(subId)) + .appendEncodedPath(field).build(); + } + + /** + * Generates a content {@link Uri} used to receive updates on every field in the + * ServiceState provider. + * <p> + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * {@link ServiceState} while your app is running. You can also use a {@link JobService} to + * ensure your app is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link JobService} does not guarantee timely delivery of + * updates to the {@link Uri}. + * + * @param subId the subId to receive updates on + * @return the Uri used to observe {@link ServiceState} changes + */ + public static Uri getUriForSubId(int subId) { + return CONTENT_URI.buildUpon().appendEncodedPath(String.valueOf(subId)).build(); + } + + /** + * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance. + * + * @param state the ServiceState to convert into ContentValues + * @return the convertedContentValues instance + * @hide + */ + public static ContentValues getContentValuesForServiceState(ServiceState state) { + ContentValues values = new ContentValues(); + values.put(VOICE_REG_STATE, state.getVoiceRegState()); + values.put(DATA_REG_STATE, state.getDataRegState()); + values.put(VOICE_ROAMING_TYPE, state.getVoiceRoamingType()); + values.put(DATA_ROAMING_TYPE, state.getDataRoamingType()); + values.put(VOICE_OPERATOR_ALPHA_LONG, state.getVoiceOperatorAlphaLong()); + values.put(VOICE_OPERATOR_ALPHA_SHORT, state.getVoiceOperatorAlphaShort()); + values.put(VOICE_OPERATOR_NUMERIC, state.getVoiceOperatorNumeric()); + values.put(DATA_OPERATOR_ALPHA_LONG, state.getDataOperatorAlphaLong()); + values.put(DATA_OPERATOR_ALPHA_SHORT, state.getDataOperatorAlphaShort()); + values.put(DATA_OPERATOR_NUMERIC, state.getDataOperatorNumeric()); + values.put(IS_MANUAL_NETWORK_SELECTION, state.getIsManualSelection()); + values.put(RIL_VOICE_RADIO_TECHNOLOGY, state.getRilVoiceRadioTechnology()); + values.put(RIL_DATA_RADIO_TECHNOLOGY, state.getRilDataRadioTechnology()); + values.put(CSS_INDICATOR, state.getCssIndicator()); + values.put(NETWORK_ID, state.getNetworkId()); + values.put(SYSTEM_ID, state.getSystemId()); + values.put(CDMA_ROAMING_INDICATOR, state.getCdmaRoamingIndicator()); + values.put(CDMA_DEFAULT_ROAMING_INDICATOR, state.getCdmaDefaultRoamingIndicator()); + values.put(CDMA_ERI_ICON_INDEX, state.getCdmaEriIconIndex()); + values.put(CDMA_ERI_ICON_MODE, state.getCdmaEriIconMode()); + values.put(IS_EMERGENCY_ONLY, state.isEmergencyOnly()); + values.put(IS_DATA_ROAMING_FROM_REGISTRATION, state.getDataRoamingFromRegistration()); + values.put(IS_USING_CARRIER_AGGREGATION, state.isUsingCarrierAggregation()); + return values; + } + + /** + * An integer value indicating the current voice service state. + * <p> + * Valid values: {@link ServiceState#STATE_IN_SERVICE}, + * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY}, + * {@link ServiceState#STATE_POWER_OFF}. + * <p> + * This is the same as {@link ServiceState#getState()}. + */ + public static final String VOICE_REG_STATE = "voice_reg_state"; + + /** + * An integer value indicating the current data service state. + * <p> + * Valid values: {@link ServiceState#STATE_IN_SERVICE}, + * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY}, + * {@link ServiceState#STATE_POWER_OFF}. + * <p> + * This is the same as {@link ServiceState#getDataRegState()}. + * @hide + */ + public static final String DATA_REG_STATE = "data_reg_state"; + + /** + * An integer value indicating the current voice roaming type. + * <p> + * This is the same as {@link ServiceState#getVoiceRoamingType()}. + * @hide + */ + public static final String VOICE_ROAMING_TYPE = "voice_roaming_type"; + + /** + * An integer value indicating the current data roaming type. + * <p> + * This is the same as {@link ServiceState#getDataRoamingType()}. + * @hide + */ + public static final String DATA_ROAMING_TYPE = "data_roaming_type"; + + /** + * The current registered voice network operator name in long alphanumeric format. + * <p> + * This is the same as {@link ServiceState#getVoiceOperatorAlphaLong()}. + * @hide + */ + public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long"; + + /** + * The current registered operator name in short alphanumeric format. + * <p> + * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice + * network operator name in long alphanumeric format. + * <p> + * This is the same as {@link ServiceState#getVoiceOperatorAlphaShort()}. + * @hide + */ + public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short"; + + + /** + * The current registered operator numeric id. + * <p> + * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit + * network code. + * <p> + * This is the same as {@link ServiceState#getOperatorNumeric()}. + */ + public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; + + /** + * The current registered data network operator name in long alphanumeric format. + * <p> + * This is the same as {@link ServiceState#getDataOperatorAlphaLong()}. + * @hide + */ + public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long"; + + /** + * The current registered data network operator name in short alphanumeric format. + * <p> + * This is the same as {@link ServiceState#getDataOperatorAlphaShort()}. + * @hide + */ + public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short"; + + /** + * The current registered data network operator numeric id. + * <p> + * This is the same as {@link ServiceState#getDataOperatorNumeric()}. + * @hide + */ + public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric"; + + /** + * The current network selection mode. + * <p> + * This is the same as {@link ServiceState#getIsManualSelection()}. + */ + public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection"; + + /** + * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}. + * @hide + */ + public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology"; + + /** + * This is the same as {@link ServiceState#getRilDataRadioTechnology()}. + * @hide + */ + public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology"; + + /** + * This is the same as {@link ServiceState#getCssIndicator()}. + * @hide + */ + public static final String CSS_INDICATOR = "css_indicator"; + + /** + * This is the same as {@link ServiceState#getNetworkId()}. + * @hide + */ + public static final String NETWORK_ID = "network_id"; + + /** + * This is the same as {@link ServiceState#getSystemId()}. + * @hide + */ + public static final String SYSTEM_ID = "system_id"; + + /** + * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}. + * @hide + */ + public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator"; + + /** + * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}. + * @hide + */ + public static final String CDMA_DEFAULT_ROAMING_INDICATOR = + "cdma_default_roaming_indicator"; + + /** + * This is the same as {@link ServiceState#getCdmaEriIconIndex()}. + * @hide + */ + public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index"; + + /** + * This is the same as {@link ServiceState#getCdmaEriIconMode()}. + * @hide + */ + public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode"; + + /** + * This is the same as {@link ServiceState#isEmergencyOnly()}. + * @hide + */ + public static final String IS_EMERGENCY_ONLY = "is_emergency_only"; + + /** + * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}. + * @hide + */ + public static final String IS_DATA_ROAMING_FROM_REGISTRATION = + "is_data_roaming_from_registration"; + + /** + * This is the same as {@link ServiceState#isUsingCarrierAggregation()}. + * @hide + */ + public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation"; + } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 397aa0020d13..8ee6454ebd66 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -911,7 +911,7 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getDeviceSoftwareVersion() { - return getDeviceSoftwareVersion(getDefaultSim()); + return getDeviceSoftwareVersion(getSlotIndex()); } /** @@ -997,7 +997,7 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getImei() { - return getImei(getDefaultSim()); + return getImei(getSlotIndex()); } /** @@ -1029,7 +1029,7 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getMeid() { - return getMeid(getDefaultSim()); + return getMeid(getSlotIndex()); } /** @@ -1059,7 +1059,7 @@ public class TelephonyManager { */ /** {@hide}*/ public String getNai() { - return getNai(getDefaultSim()); + return getNai(getSlotIndex()); } /** @@ -1303,7 +1303,7 @@ public class TelephonyManager { } private int getPhoneTypeFromProperty() { - return getPhoneTypeFromProperty(getDefaultPhone()); + return getPhoneTypeFromProperty(getPhoneId()); } /** {@hide} */ @@ -1317,7 +1317,7 @@ public class TelephonyManager { } private int getPhoneTypeFromNetworkType() { - return getPhoneTypeFromNetworkType(getDefaultPhone()); + return getPhoneTypeFromNetworkType(getPhoneId()); } /** {@hide} */ @@ -1500,7 +1500,7 @@ public class TelephonyManager { * on a CDMA network). */ public String getNetworkOperator() { - return getNetworkOperatorForPhone(getDefaultPhone()); + return getNetworkOperatorForPhone(getPhoneId()); } /** @@ -1546,7 +1546,7 @@ public class TelephonyManager { * @see #createForPhoneAccountHandle(PhoneAccountHandle) */ public String getNetworkSpecifier() { - return String.valueOf(mSubId); + return String.valueOf(getSubId()); } /** @@ -1565,7 +1565,7 @@ public class TelephonyManager { public PersistableBundle getCarrierConfig() { CarrierConfigManager carrierConfigManager = mContext .getSystemService(CarrierConfigManager.class); - return carrierConfigManager.getConfigForSubId(mSubId); + return carrierConfigManager.getConfigForSubId(getSubId()); } /** @@ -1602,7 +1602,7 @@ public class TelephonyManager { * on a CDMA network). */ public String getNetworkCountryIso() { - return getNetworkCountryIsoForPhone(getDefaultPhone()); + return getNetworkCountryIsoForPhone(getPhoneId()); } /** @@ -1748,6 +1748,9 @@ public class TelephonyManager { * Returns a constant indicating the radio technology (network type) * currently in use on the device for data transmission. * + * If this object has been created with {@link #createForSubscriptionId}, applies to the given + * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -1772,7 +1775,7 @@ public class TelephonyManager { * @see #NETWORK_TYPE_HSPAP */ public int getDataNetworkType() { - return getDataNetworkType(getSubId()); + return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); } /** @@ -1996,7 +1999,7 @@ public class TelephonyManager { * @return true if a ICC card is present */ public boolean hasIccCard() { - return hasIccCard(getDefaultSim()); + return hasIccCard(getSlotIndex()); } /** @@ -2037,7 +2040,7 @@ public class TelephonyManager { * @see #SIM_STATE_CARD_RESTRICTED */ public int getSimState() { - int slotIndex = getDefaultSim(); + int slotIndex = getSlotIndex(); // slotIndex may be invalid due to sim being absent. In that case query all slots to get // sim state if (slotIndex < 0) { @@ -2166,7 +2169,7 @@ public class TelephonyManager { * @see #getSimState */ public String getSimOperatorName() { - return getSimOperatorNameForPhone(getDefaultPhone()); + return getSimOperatorNameForPhone(getPhoneId()); } /** @@ -2198,7 +2201,7 @@ public class TelephonyManager { * Returns the ISO country code equivalent for the SIM provider's country code. */ public String getSimCountryIso() { - return getSimCountryIsoForPhone(getDefaultPhone()); + return getSimCountryIsoForPhone(getPhoneId()); } /** @@ -2754,7 +2757,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony - .getVisualVoicemailPackageName(mContext.getOpPackageName(), mSubId); + .getVisualVoicemailPackageName(mContext.getOpPackageName(), getSubId()); } } catch (RemoteException ex) { } catch (NullPointerException ex) { @@ -4057,35 +4060,67 @@ public class TelephonyManager { * subId is returned. Otherwise, the default subId will be returned. */ private int getSubId() { - if (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { - return getDefaultSubscription(); + if (SubscriptionManager.isUsableSubIdValue(mSubId)) { + return mSubId; } - return mSubId; + return SubscriptionManager.getDefaultSubscriptionId(); + } + + /** + * Return an appropriate subscription ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the provided + * subId is returned. Otherwise, the preferred subId which is based on caller's context is + * returned. + * {@see SubscriptionManager#getDefaultDataSubscriptionId()} + * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()} + * {@see SubscriptionManager#getDefaultSmsSubscriptionId()} + */ + private int getSubId(int preferredSubId) { + if (SubscriptionManager.isUsableSubIdValue(mSubId)) { + return mSubId; + } + return preferredSubId; } /** - * Returns Default subscription. + * Return an appropriate phone ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the phoneId + * associated with the provided subId is returned. Otherwise, the default phoneId associated + * with the default subId will be returned. */ - private static int getDefaultSubscription() { - return SubscriptionManager.getDefaultSubscriptionId(); + private int getPhoneId() { + return SubscriptionManager.getPhoneId(getSubId()); } /** - * Returns Default phone. + * Return an appropriate phone ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the phoneId + * associated with the provided subId is returned. Otherwise, return the phoneId associated + * with the preferred subId based on caller's context. + * {@see SubscriptionManager#getDefaultDataSubscriptionId()} + * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()} + * {@see SubscriptionManager#getDefaultSmsSubscriptionId()} */ - private static int getDefaultPhone() { - return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId()); + private int getPhoneId(int preferredSubId) { + return SubscriptionManager.getPhoneId(getSubId(preferredSubId)); } /** - * @return default SIM's slot index. If SIM is not inserted, return default SIM slot index. + * Return an appropriate slot index for any situation. + * + * if this object has been created with {@link #createForSubscriptionId}, then the slot index + * associated with the provided subId is returned. Otherwise, return the slot index associated + * with the default subId. + * If SIM is not inserted, return default SIM slot index. * * {@hide} */ @VisibleForTesting - public int getDefaultSim() { - int slotIndex = SubscriptionManager.getSlotIndex( - SubscriptionManager.getDefaultSubscriptionId()); + public int getSlotIndex() { + int slotIndex = SubscriptionManager.getSlotIndex(getSubId()); if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) { slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX; } @@ -4393,7 +4428,7 @@ public class TelephonyManager { * Returns null if the query fails. * * - * <p>Requires that the caller has READ_PRIVILEGED_PHONE_STATE + * <p>Requires that the caller has READ_PHONE_STATE * * @return an array of forbidden PLMNs or null if not available */ @@ -4406,7 +4441,7 @@ public class TelephonyManager { * Returns null if the query fails. * * - * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE + * <p>Requires that the calling app has READ_PHONE_STATE * * @param subId subscription ID used for authentication * @param appType the icc application type, like {@link #APPTYPE_USIM} @@ -4902,7 +4937,7 @@ public class TelephonyManager { /** @hide */ @SystemApi public List<String> getCarrierPackageNamesForIntent(Intent intent) { - return getCarrierPackageNamesForIntentAndPhone(intent, getDefaultPhone()); + return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId()); } /** @hide */ @@ -5164,7 +5199,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.handleUssdRequest(mSubId, ussdRequest, wrappedCallback); + telephony.handleUssdRequest(getSubId(), ussdRequest, wrappedCallback); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e); @@ -5184,7 +5219,8 @@ public class TelephonyManager { public boolean isConcurrentVoiceAndDataSupported() { try { ITelephony telephony = getITelephony(); - return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(mSubId)); + return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed( + getSubId())); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e); } @@ -5321,6 +5357,8 @@ public class TelephonyManager { /** * Turns mobile data on or off. + * If this object has been created with {@link #createForSubscriptionId}, applies to the given + * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the @@ -5331,7 +5369,7 @@ public class TelephonyManager { * @see #hasCarrierPrivileges */ public void setDataEnabled(boolean enable) { - setDataEnabled(getSubId(), enable); + setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable); } /** @hide */ @@ -5361,6 +5399,9 @@ public class TelephonyManager { /** * Returns whether mobile data is enabled or not. * + * If this object has been created with {@link #createForSubscriptionId}, applies to the given + * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * * <p>Requires one of the following permissions: * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE}, * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the @@ -5376,7 +5417,7 @@ public class TelephonyManager { */ @SuppressWarnings("deprecation") public boolean isDataEnabled() { - return getDataEnabled(getSubId()); + return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); } /** @@ -5626,7 +5667,7 @@ public class TelephonyManager { * @hide */ public void setSimOperatorNumeric(String numeric) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setSimOperatorNumericForPhone(phoneId, numeric); } @@ -5646,7 +5687,7 @@ public class TelephonyManager { * @hide */ public void setSimOperatorName(String name) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setSimOperatorNameForPhone(phoneId, name); } @@ -5666,7 +5707,7 @@ public class TelephonyManager { * @hide */ public void setSimCountryIso(String iso) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setSimCountryIsoForPhone(phoneId, iso); } @@ -5686,7 +5727,7 @@ public class TelephonyManager { * @hide */ public void setSimState(String state) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setSimStateForPhone(phoneId, state); } @@ -5711,7 +5752,7 @@ public class TelephonyManager { * @hide **/ public void setSimPowerState(boolean powerUp) { - setSimPowerStateForSlot(getDefaultSim(), powerUp); + setSimPowerStateForSlot(getSlotIndex(), powerUp); } /** @@ -5745,7 +5786,7 @@ public class TelephonyManager { * @hide */ public void setBasebandVersion(String version) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setBasebandVersionForPhone(phoneId, version); } @@ -5772,7 +5813,7 @@ public class TelephonyManager { * @hide */ public void setPhoneType(int type) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setPhoneType(phoneId, type); } @@ -5800,7 +5841,7 @@ public class TelephonyManager { * @hide */ public String getOtaSpNumberSchema(String defaultValue) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); return getOtaSpNumberSchemaForPhone(phoneId, defaultValue); } @@ -5831,7 +5872,7 @@ public class TelephonyManager { * @hide */ public boolean getSmsReceiveCapable(boolean defaultValue) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); return getSmsReceiveCapableForPhone(phoneId, defaultValue); } @@ -5862,7 +5903,7 @@ public class TelephonyManager { * @hide */ public boolean getSmsSendCapable(boolean defaultValue) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); return getSmsSendCapableForPhone(phoneId, defaultValue); } @@ -5890,7 +5931,7 @@ public class TelephonyManager { * @hide */ public void setNetworkOperatorName(String name) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setNetworkOperatorNameForPhone(phoneId, name); } @@ -5912,7 +5953,7 @@ public class TelephonyManager { * @hide */ public void setNetworkOperatorNumeric(String numeric) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setNetworkOperatorNumericForPhone(phoneId, numeric); } @@ -5932,7 +5973,7 @@ public class TelephonyManager { * @hide */ public void setNetworkRoaming(boolean isRoaming) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setNetworkRoamingForPhone(phoneId, isRoaming); } @@ -5956,7 +5997,7 @@ public class TelephonyManager { * @hide */ public void setNetworkCountryIso(String iso) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(); setNetworkCountryIsoForPhone(phoneId, iso); } @@ -5976,11 +6017,15 @@ public class TelephonyManager { /** * Set the network type currently in use on the device for data transmission. + * + * If this object has been created with {@link #createForSubscriptionId}, applies to the + * phoneId associated with the given subId. Otherwise, applies to the phoneId associated with + * {@link SubscriptionManager#getDefaultDataSubscriptionId()} * @param type the network type currently in use on the device for data transmission * @hide */ public void setDataNetworkType(int type) { - int phoneId = getDefaultPhone(); + int phoneId = getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId()); setDataNetworkTypeForPhone(phoneId, type); } @@ -6200,7 +6245,7 @@ public class TelephonyManager { * @hide */ public String getAidForAppType(int appType) { - return getAidForAppType(getDefaultSubscription(), appType); + return getAidForAppType(getSubId(), appType); } /** @@ -6234,7 +6279,7 @@ public class TelephonyManager { * @hide */ public String getEsn() { - return getEsn(getDefaultSubscription()); + return getEsn(getSubId()); } /** @@ -6267,7 +6312,7 @@ public class TelephonyManager { * @hide */ public String getCdmaPrlVersion() { - return getCdmaPrlVersion(getDefaultSubscription()); + return getCdmaPrlVersion(getSubId()); } /** diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index a8eb9862202d..b4e3a476425a 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -530,13 +530,6 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } - /** STOPSHIP remove when trial API is turned down */ - @Override - public ComponentName startServiceInForeground(Intent service, - int id, Notification notification) { - throw new UnsupportedOperationException(); - } - @Override public boolean stopService(Intent service) { throw new UnsupportedOperationException(); @@ -554,13 +547,6 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } - /** @hide STOPSHIP removed when trial API is turned down */ - @Override - public ComponentName startServiceInForegroundAsUser(Intent service, - int id, Notification notification, UserHandle user) { - throw new UnsupportedOperationException(); - } - /** @hide */ @Override public boolean stopServiceAsUser(Intent service, UserHandle user) { diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 20392e733599..2d3d79a03446 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -371,12 +371,30 @@ public class MockPackageManager extends PackageManager { /** @hide */ @Override + public int getInstantAppCookieMaxBytes() { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override public int getInstantAppCookieMaxSize() { throw new UnsupportedOperationException(); } /** @hide */ @Override + public void clearInstantAppCookie() { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override + public void updateInstantAppCookie(@NonNull byte[] cookie) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override public boolean setInstantAppCookie(@NonNull byte[] cookie) { throw new UnsupportedOperationException(); } @@ -1134,4 +1152,11 @@ public class MockPackageManager extends PackageManager { public ComponentName getInstantAppInstallerComponent() { throw new UnsupportedOperationException(); } + + /** + * @hide + */ + public String getInstantAppAndroidId(String packageName, UserHandle user) { + throw new UnsupportedOperationException(); + } } diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java new file mode 100644 index 000000000000..e3b06c875236 --- /dev/null +++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES; +import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +import android.net.NetworkCapabilities; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkCapabilitiesTest { + @Test + public void testMaybeMarkCapabilitiesRestricted() { + // verify EIMS is restricted + assertEquals((1 << NET_CAPABILITY_EIMS) & RESTRICTED_CAPABILITIES, + (1 << NET_CAPABILITY_EIMS)); + + // verify CBS is also restricted + assertEquals((1 << NET_CAPABILITY_CBS) & RESTRICTED_CAPABILITIES, + (1 << NET_CAPABILITY_CBS)); + + // verify default is not restricted + assertEquals((1 << NET_CAPABILITY_INTERNET) & RESTRICTED_CAPABILITIES, 0); + + // just to see + assertEquals(RESTRICTED_CAPABILITIES & UNRESTRICTED_CAPABILITIES, 0); + + // check that internet does not get restricted + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // metered-ness shouldn't matter + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // add EIMS - bundled with unrestricted means it's unrestricted + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // just a restricted cap should be restricted regardless of meteredness + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // try 2 restricted caps + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_CBS); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_CBS); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + } + +} diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 2bf52066b618..bd2b2a36788f 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -782,7 +782,79 @@ static void fullyQualifyClassName(const String8& package, const sp<XMLNode>& nod } } -status_t massageManifest(Bundle* bundle, sp<XMLNode> root) +static sp<ResourceTable::ConfigList> findEntry(const String16& packageStr, const String16& typeStr, + const String16& nameStr, ResourceTable* table) { + sp<ResourceTable::Package> pkg = table->getPackage(packageStr); + if (pkg != NULL) { + sp<ResourceTable::Type> type = pkg->getTypes().valueFor(typeStr); + if (type != NULL) { + return type->getConfigs().valueFor(nameStr); + } + } + return NULL; +} + +static uint16_t getMaxSdkVersion(const sp<ResourceTable::ConfigList>& configList) { + const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries = + configList->getEntries(); + uint16_t maxSdkVersion = 0u; + for (size_t i = 0; i < entries.size(); i++) { + maxSdkVersion = std::max(maxSdkVersion, entries.keyAt(i).sdkVersion); + } + return maxSdkVersion; +} + +static void massageRoundIconSupport(const String16& iconRef, const String16& roundIconRef, + ResourceTable* table) { + bool publicOnly = false; + const char* err; + + String16 iconPackage, iconType, iconName; + if (!ResTable::expandResourceRef(iconRef.string(), iconRef.size(), &iconPackage, &iconType, + &iconName, NULL, &table->getAssetsPackage(), &err, + &publicOnly)) { + // Errors will be raised in later XML compilation. + return; + } + + sp<ResourceTable::ConfigList> iconEntry = findEntry(iconPackage, iconType, iconName, table); + if (iconEntry == NULL || getMaxSdkVersion(iconEntry) < SDK_O) { + // The icon is not adaptive, so nothing to massage. + return; + } + + String16 roundIconPackage, roundIconType, roundIconName; + if (!ResTable::expandResourceRef(roundIconRef.string(), roundIconRef.size(), &roundIconPackage, + &roundIconType, &roundIconName, NULL, &table->getAssetsPackage(), + &err, &publicOnly)) { + // Errors will be raised in later XML compilation. + return; + } + + sp<ResourceTable::ConfigList> roundIconEntry = findEntry(roundIconPackage, roundIconType, + roundIconName, table); + if (roundIconEntry == NULL || getMaxSdkVersion(roundIconEntry) >= SDK_O) { + // The developer explicitly used a v26 compatible drawable as the roundIcon, meaning we should + // not generate an alias to the icon drawable. + return; + } + + String16 aliasValue = String16(String8::format("@%s:%s/%s", String8(iconPackage).string(), + String8(iconType).string(), + String8(iconName).string())); + + // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon. + const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& configList = + iconEntry->getEntries(); + for (size_t i = 0; i < configList.size(); i++) { + if (configList.keyAt(i).sdkVersion >= SDK_O) { + table->addEntry(SourcePos(), roundIconPackage, roundIconType, roundIconName, aliasValue, + NULL, &configList.keyAt(i)); + } + } +} + +status_t massageManifest(Bundle* bundle, ResourceTable* table, sp<XMLNode> root) { root = root->searchElement(String16(), String16("manifest")); if (root == NULL) { @@ -916,7 +988,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) String8 tag(child->getElementName()); if (tag == "instrumentation") { XMLNode::attribute_entry* attr = child->editAttribute( - String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); + String16(RESOURCES_ANDROID_NAMESPACE), String16("targetPackage")); if (attr != NULL) { attr->string.setTo(String16(instrumentationPackageNameOverride)); } @@ -924,6 +996,19 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } } + sp<XMLNode> application = root->getChildElement(String16(), String16("application")); + if (application != NULL) { + XMLNode::attribute_entry* icon_attr = application->editAttribute( + String16(RESOURCES_ANDROID_NAMESPACE), String16("icon")); + if (icon_attr != NULL) { + XMLNode::attribute_entry* round_icon_attr = application->editAttribute( + String16(RESOURCES_ANDROID_NAMESPACE), String16("roundIcon")); + if (round_icon_attr != NULL) { + massageRoundIconSupport(icon_attr->string, round_icon_attr->string, table); + } + } + } + // Generate split name if feature is present. const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName")); if (attr != NULL) { @@ -1638,7 +1723,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (manifestTree == NULL) { return UNKNOWN_ERROR; } - err = massageManifest(bundle, manifestTree); + err = massageManifest(bundle, &table, manifestTree); if (err < NO_ERROR) { return err; } diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 221f3c2cd0c3..52b93a945433 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -4832,8 +4832,7 @@ bool ResourceTable::versionForCompat(const Bundle* bundle, const String16& resou item.resPath = resPath; item.file = newFile; item.xmlRoot = root->clone(); - item.needsCompiling = false; // This step occurs after we parse/assign, so we don't need - // to do it again. + item.needsCompiling = true; mWorkQueue.push(item); // Now mark the old entry as deleted. diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index aff22d4d1364..b340fc56b793 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -590,9 +590,10 @@ public: const String16& comment, bool appendComment); + sp<Package> getPackage(const String16& package); + private: void writePublicDefinitions(const String16& package, FILE* fp, bool pub); - sp<Package> getPackage(const String16& package); sp<Type> getType(const String16& package, const String16& type, const SourcePos& pos, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 328fc0a1eaeb..1e77ac170028 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1865,13 +1865,6 @@ public class BridgeContext extends Context { } @Override - public ComponentName startServiceInForeground(Intent service, - int id, Notification notification) { - // pass - return null; - } - - @Override public boolean stopService(Intent arg0) { // pass return false; @@ -1884,13 +1877,6 @@ public class BridgeContext extends Context { } @Override - public ComponentName startServiceInForegroundAsUser(Intent service, - int id, Notification notification, UserHandle user) { - // pass - return null; - } - - @Override public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) { // pass return false; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index ad26bc8b8672..764eeebaef6a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -17,6 +17,7 @@ package com.android.layoutlib.bridge.android; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Intent; @@ -328,11 +329,26 @@ public class BridgePackageManager extends PackageManager { } @Override + public int getInstantAppCookieMaxBytes() { + return 0; + } + + @Override public int getInstantAppCookieMaxSize() { return 0; } @Override + public void clearInstantAppCookie() {; + + } + + @Override + public void updateInstantAppCookie(@Nullable byte[] cookie) { + + } + + @Override public boolean setInstantAppCookie(@NonNull byte[] cookie) { return false; } @@ -928,4 +944,9 @@ public class BridgePackageManager extends PackageManager { public ComponentName getInstantAppInstallerComponent() { return null; } + + @Override + public String getInstantAppAndroidId(String packageName, UserHandle user) { + return null; + } } diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index 82b3792a331c..bf5c42b8ca1c 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -19,6 +19,7 @@ package android.net.wifi.aware; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.net.NetworkSpecifier; import android.net.wifi.RttManager; import android.util.Log; @@ -250,8 +251,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * unencrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -276,13 +277,13 @@ public class DiscoverySession { * request from only that peer. A RESPONDER may specify a {@code null} - * indicating that it will accept connection requests from any device. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) { + public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) { if (mTerminated) { Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session"); return null; @@ -302,8 +303,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -329,14 +330,14 @@ public class DiscoverySession { * {@link #createNetworkSpecifierOpen(PeerHandle)} API to * specify an open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierPassphrase(@Nullable PeerHandle peerHandle, - @NonNull String passphrase) { + public NetworkSpecifier createNetworkSpecifierPassphrase( + @Nullable PeerHandle peerHandle, @NonNull String passphrase) { if (passphrase == null || passphrase.length() == 0) { throw new IllegalArgumentException("Passphrase must not be null or empty"); } @@ -361,8 +362,8 @@ public class DiscoverySession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -389,8 +390,8 @@ public class DiscoverySession { * Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an * open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. @@ -398,7 +399,7 @@ public class DiscoverySession { * @hide */ @SystemApi - public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle, + public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle, @NonNull byte[] pmk) { if (pmk == null || pmk.length == 0) { throw new IllegalArgumentException("PMK must not be null or empty"); diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 4d3957aece91..3fcbd4b60259 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -24,6 +24,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; +import android.net.NetworkSpecifier; import android.net.wifi.RttManager; import android.os.Binder; import android.os.Bundle; @@ -31,7 +32,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.util.Base64; import android.util.Log; import android.util.SparseArray; @@ -39,9 +39,6 @@ import com.android.internal.annotations.GuardedBy; import libcore.util.HexEncoding; -import org.json.JSONException; -import org.json.JSONObject; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -129,65 +126,6 @@ public class WifiAwareManager { private static final boolean VDBG = false; // STOPSHIP if true /** - * Keys used to generate a Network Specifier for the Aware network request. The network - * specifier is formatted as a JSON string. - */ - - /** - * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_IB = 0; - - /** - * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional - * [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; - - /** - * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; - - /** - * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional - * [only permitted for RESPONDER] - * @hide - */ - public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; - - - /** @hide */ - public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_TYPE = "type"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_ROLE = "role"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_CLIENT_ID = "client_id"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_SESSION_ID = "session_id"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PEER_ID = "peer_id"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk"; - - /** @hide */ - public static final String NETWORK_SPECIFIER_KEY_PASSPHRASE = "passphrase"; - - /** * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed. * Use the {@link #isAvailable()} to query the current status. * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering @@ -483,7 +421,7 @@ public class WifiAwareManager { } /** @hide */ - public String createNetworkSpecifier(int clientId, int role, int sessionId, + public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId @@ -492,9 +430,6 @@ public class WifiAwareManager { + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); } - int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER - : NETWORK_SPECIFIER_TYPE_IB; - if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { throw new IllegalArgumentException( @@ -509,35 +444,20 @@ public class WifiAwareManager { } } - JSONObject json; - try { - json = new JSONObject(); - json.put(NETWORK_SPECIFIER_KEY_TYPE, type); - json.put(NETWORK_SPECIFIER_KEY_ROLE, role); - json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId); - json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId); - if (peerHandle != null) { - json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId); - } - if (pmk == null) { - pmk = new byte[0]; - } - json.put(NETWORK_SPECIFIER_KEY_PMK, - Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT)); - if (passphrase == null) { - passphrase = new String(); - } - json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase); - - } catch (JSONException e) { - return ""; - } - - return json.toString(); + return new WifiAwareNetworkSpecifier( + (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER + : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, + role, + clientId, + sessionId, + peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID + null, // peerMac (not used in this method) + pmk, + passphrase); } /** @hide */ - public String createNetworkSpecifier(int clientId, @DataPathRole int role, + public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role, @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role @@ -545,9 +465,6 @@ public class WifiAwareManager { + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); } - int type = (peer == null) ? - NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB; - if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { throw new IllegalArgumentException( @@ -564,29 +481,16 @@ public class WifiAwareManager { throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address"); } - JSONObject json; - try { - json = new JSONObject(); - json.put(NETWORK_SPECIFIER_KEY_TYPE, type); - json.put(NETWORK_SPECIFIER_KEY_ROLE, role); - json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId); - if (peer != null) { - json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer))); - } - if (pmk == null) { - pmk = new byte[0]; - } - json.put(NETWORK_SPECIFIER_KEY_PMK, - Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT)); - if (passphrase == null) { - passphrase = new String(); - } - json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase); - } catch (JSONException e) { - return ""; - } - - return json.toString(); + return new WifiAwareNetworkSpecifier( + (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER + : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, + role, + clientId, + 0, // 0 is an invalid session ID + 0, // 0 is an invalid peer ID + peer, + pmk, + passphrase); } private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java new file mode 100644 index 000000000000..59934806f398 --- /dev/null +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.aware; + +import android.net.NetworkSpecifier; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects + * directly but obtain them using + * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or + * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase) + * versions. + * + * @hide + */ +public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { + /** + * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_IB = 0; + + /** + * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional + * [only permitted for RESPONDER] + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; + + /** + * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; + + /** + * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional + * [only permitted for RESPONDER] + * @hide + */ + public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; + + /** @hide */ + public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; + + /** + * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object. + * @hide + */ + public final int type; + + /** + * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or + * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER. + * @hide + */ + public final int role; + + /** + * The client ID of the device. + * @hide + */ + public final int clientId; + + /** + * The session ID in which context to request a data-path. Only relevant for IB requests. + * @hide + */ + public final int sessionId; + + /** + * The peer ID of the device which the data-path should be connected to. Only relevant for + * IB requests (i.e. not IB_ANY_PEER or OOB*). + * @hide + */ + public final int peerId; + + /** + * The peer MAC address of the device which the data-path should be connected to. Only relevant + * for OB requests (i.e. not OOB_ANY_PEER or IB*). + * @hide + */ + public final byte[] peerMac; + + /** + * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should + * be specified. + * @hide + */ + public final byte[] pmk; + + /** + * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or + * passphrase should be specified. + * @hide + */ + public final String passphrase; + + /** @hide */ + public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, + byte[] peerMac, byte[] pmk, String passphrase) { + this.type = type; + this.role = role; + this.clientId = clientId; + this.sessionId = sessionId; + this.peerId = peerId; + this.peerMac = peerMac; + this.pmk = pmk; + this.passphrase = passphrase; + } + + public static final Creator<WifiAwareNetworkSpecifier> CREATOR = + new Creator<WifiAwareNetworkSpecifier>() { + @Override + public WifiAwareNetworkSpecifier createFromParcel(Parcel in) { + return new WifiAwareNetworkSpecifier( + in.readInt(), // type + in.readInt(), // role + in.readInt(), // clientId + in.readInt(), // sessionId + in.readInt(), // peerId + in.createByteArray(), // peerMac + in.createByteArray(), // pmk + in.readString()); // passphrase + } + + @Override + public WifiAwareNetworkSpecifier[] newArray(int size) { + return new WifiAwareNetworkSpecifier[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(type); + dest.writeInt(role); + dest.writeInt(clientId); + dest.writeInt(sessionId); + dest.writeInt(peerId); + dest.writeByteArray(peerMac); + dest.writeByteArray(pmk); + dest.writeString(passphrase); + } + + /** @hide */ + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier. + return equals(other); + } + + /** @hide */ + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + type; + result = 31 * result + role; + result = 31 * result + clientId; + result = 31 * result + sessionId; + result = 31 * result + peerId; + result = 31 * result + Arrays.hashCode(peerMac); + result = 31 * result + Arrays.hashCode(pmk); + result = 31 * result + Objects.hashCode(passphrase); + + return result; + } + + /** @hide */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof WifiAwareNetworkSpecifier)) { + return false; + } + + WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj; + + return type == lhs.type + && role == lhs.role + && clientId == lhs.clientId + && sessionId == lhs.sessionId + && peerId == lhs.peerId + && Arrays.equals(peerMac, lhs.peerMac) + && Arrays.equals(pmk, lhs.pmk) + && Objects.equals(passphrase, lhs.passphrase); + } + + /** @hide */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier ["); + sb.append("type=").append(type) + .append(", role=").append(role) + .append(", clientId=").append(clientId) + .append(", sessionId=").append(sessionId) + .append(", peerId=").append(peerId) + // masking potential PII (although low impact information) + .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>") + // masking PII + .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>") + // masking PII + .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>") + .append("]"); + return sb.toString(); + } +} diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java index 895defb9a540..ac3a6bba052c 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java @@ -19,6 +19,7 @@ package android.net.wifi.aware; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.net.NetworkSpecifier; import android.os.Binder; import android.os.Handler; import android.os.Looper; @@ -184,8 +185,8 @@ public class WifiAwareSession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * unencrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -205,29 +206,29 @@ public class WifiAwareSession { * peer. A RESPONDER may specify a {@code null} - indicating that it will accept * connection requests from any device. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer) { + public NetworkSpecifier createNetworkSpecifierOpen( + @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager"); - return ""; + return null; } if (mTerminated) { Log.e(TAG, "createNetworkSpecifierOpen: called after termination"); - return ""; + return null; } return mgr.createNetworkSpecifier(mClientId, role, peer, null, null); } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -247,22 +248,23 @@ public class WifiAwareSession { * the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to * specify an open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifierPassphrase(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer, @NonNull String passphrase) { + public NetworkSpecifier createNetworkSpecifierPassphrase( + @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, + @NonNull String passphrase) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager"); - return ""; + return null; } if (mTerminated) { Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination"); - return ""; + return null; } if (passphrase == null || passphrase.length() == 0) { throw new IllegalArgumentException("Passphrase must not be null or empty"); @@ -271,8 +273,8 @@ public class WifiAwareSession { } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an - * encrypted WiFi Aware connection (link) to the specified peer. The + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for + * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> @@ -294,8 +296,8 @@ public class WifiAwareSession { * Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} to specify an * open (unencrypted) link. * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * @return A {@link NetworkSpecifier} to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. @@ -303,16 +305,16 @@ public class WifiAwareSession { * @hide */ @SystemApi - public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role, - @Nullable byte[] peer, @NonNull byte[] pmk) { + public NetworkSpecifier createNetworkSpecifierPmk( + @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, @NonNull byte[] pmk) { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); - return ""; + return null; } if (mTerminated) { Log.e(TAG, "createNetworkSpecifierPmk: called after termination"); - return ""; + return null; } if (pmk == null || pmk.length == 0) { throw new IllegalArgumentException("PMK must not be null or empty"); diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 830db22929e8..72a6a7a7b46b 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -34,11 +34,9 @@ import android.os.IBinder; import android.os.Parcel; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Base64; import libcore.util.HexEncoding; -import org.json.JSONObject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -959,8 +957,6 @@ public class WifiAwareManagerTest { final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); - String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT); - ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor @@ -991,51 +987,37 @@ public class WifiAwareManagerTest { inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture()); // (3) request an open (unencrypted) network specifier from the session - String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle); + WifiAwareNetworkSpecifier ns = + (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierOpen( + peerHandle); // validate format - JSONObject jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("session_id", sessionId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); - collector.checkThat("peer_id", peerHandle.peerId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); + collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); // (4) request an encrypted (PMK) network specifier from the session - networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk); + ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk( + peerHandle, pmk); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("session_id", sessionId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); - collector.checkThat("peer_id", peerHandle.peerId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); - collector.checkThat("pmk", pmkB64 , - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); + collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); + collector.checkThat("pmk", pmk , equalTo(ns.pmk)); // (5) request an encrypted (Passphrase) network specifier from the session - networkSpecifier = publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle, - passphrase); + ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase( + peerHandle, passphrase); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("session_id", sessionId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID))); - collector.checkThat("peer_id", peerHandle.peerId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID))); - collector.checkThat("passphrase", passphrase, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); + collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); + collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); @@ -1054,8 +1036,6 @@ public class WifiAwareManagerTest { final byte[] pmk = "Some arbitrary pmk data".getBytes(); final String passphrase = "A really bad password"; - String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT); - ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( WifiAwareSession.class); ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor @@ -1074,47 +1054,32 @@ public class WifiAwareManagerTest { WifiAwareSession session = sessionCaptor.getValue(); // (2) request an open (unencrypted) direct network specifier - String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac); + WifiAwareNetworkSpecifier ns = + (WifiAwareNetworkSpecifier) session.createNetworkSpecifierOpen(role, someMac); // validate format - JSONObject jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( - jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), - false))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac)); // (3) request an encrypted (PMK) direct network specifier - networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk); + ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPmk(role, someMac, pmk); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( - jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), - false))); - collector.checkThat("pmk", pmkB64, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac)); + collector.checkThat("pmk", pmk, equalTo(ns.pmk)); // (4) request an encrypted (Passphrase) direct network specifier - networkSpecifier = session.createNetworkSpecifierPassphrase(role, someMac, passphrase); + ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPassphrase(role, someMac, + passphrase); // validate format - jsonObject = new JSONObject(networkSpecifier); - collector.checkThat("role", role, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE))); - collector.checkThat("client_id", clientId, - equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID))); - collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode( - jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(), - false))); - collector.checkThat("passphrase", passphrase, - equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE))); + collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("client_id", clientId, equalTo(ns.clientId)); + collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac)); + collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); |